Skip to content

Commit c04eca1

Browse files
Merge pull request #135 from Flagsmith/release/2.1.0
Release 2.1.0
2 parents e4b543d + 1e126d3 commit c04eca1

File tree

10 files changed

+46
-66
lines changed

10 files changed

+46
-66
lines changed

flag_engine/api/schemas.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from flag_engine.environments.schemas import (
1111
BaseEnvironmentAPIKeySchema,
1212
BaseEnvironmentSchema,
13-
WebhookSchema,
1413
)
1514
from flag_engine.features.schemas import (
1615
BaseFeatureStateSchema,
@@ -140,7 +139,6 @@ class DjangoEnvironmentSchema(BaseEnvironmentSchema):
140139
dump_only=True,
141140
)
142141
project = fields.Nested(DjangoProjectSchema, dump_only=True)
143-
webhooks = DjangoRelatedManagerField(fields.Nested(WebhookSchema), required=False)
144142

145143
@pre_dump()
146144
def set_environment_key_in_context(self, obj, *args, **kwargs):

flag_engine/environments/models.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,8 @@ def is_valid(self):
2727

2828
@dataclass
2929
class WebhookModel:
30-
created_at: datetime
31-
updated_at: datetime
3230
url: str
3331
secret: str
34-
enabled: bool = True
3532

3633

3734
@dataclass
@@ -45,7 +42,7 @@ class EnvironmentModel:
4542
mixpanel_config: IntegrationModel = None
4643
heap_config: IntegrationModel = None
4744
dynatrace_config: IntegrationModel = None
48-
webhooks: typing.List[WebhookModel] = field(default_factory=list)
45+
webhook_config: WebhookModel = None
4946

5047
_INTEGRATION_ATTS = [
5148
"amplitude_config",

flag_engine/environments/schemas.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,8 @@ class Meta:
2828

2929

3030
class WebhookSchema(LoadToModelSchema):
31-
enabled = fields.Bool()
3231
url = fields.URL()
3332
secret = fields.Str()
34-
created_at = fields.DateTime()
35-
updated_at = fields.DateTime()
3633

3734
class Meta:
3835
model_class = WebhookModel
@@ -46,14 +43,12 @@ class BaseEnvironmentSchema(Schema):
4643
mixpanel_config = fields.Nested(IntegrationSchema, required=False, allow_none=True)
4744
amplitude_config = fields.Nested(IntegrationSchema, required=False, allow_none=True)
4845
dynatrace_config = fields.Nested(IntegrationSchema, required=False, allow_none=True)
46+
webhook_config = fields.Nested(WebhookSchema, required=False, allow_none=True)
4947

5048

5149
class EnvironmentSchema(LoadToModelMixin, BaseEnvironmentSchema):
5250
feature_states = fields.List(fields.Nested(FeatureStateSchema))
5351
project = fields.Nested(ProjectSchema)
54-
webhooks = fields.List(
55-
fields.Nested(WebhookSchema), required=False, allow_none=True
56-
)
5752

5853
class Meta:
5954
model_class = EnvironmentModel

flag_engine/segments/models.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import re
22
import typing
3+
from contextlib import suppress
34
from dataclasses import dataclass, field
45

56
import semver
@@ -23,28 +24,31 @@ class SegmentConditionModel:
2324

2425
def matches_trait_value(self, trait_value: typing.Any) -> bool:
2526
# TODO: move this logic to the evaluator module
26-
if type(self.value) is str and is_semver(self.value):
27-
trait_value = semver.VersionInfo.parse(trait_value)
28-
if self.operator in self.EXCEPTION_OPERATOR_METHODS:
29-
evaluator_function = getattr(
30-
self, self.EXCEPTION_OPERATOR_METHODS.get(self.operator)
27+
with suppress(ValueError):
28+
if type(self.value) is str and is_semver(self.value):
29+
trait_value = semver.VersionInfo.parse(trait_value)
30+
if self.operator in self.EXCEPTION_OPERATOR_METHODS:
31+
evaluator_function = getattr(
32+
self, self.EXCEPTION_OPERATOR_METHODS.get(self.operator)
33+
)
34+
return evaluator_function(trait_value)
35+
36+
matching_function_name = {
37+
constants.EQUAL: "__eq__",
38+
constants.GREATER_THAN: "__gt__",
39+
constants.GREATER_THAN_INCLUSIVE: "__ge__",
40+
constants.LESS_THAN: "__lt__",
41+
constants.LESS_THAN_INCLUSIVE: "__le__",
42+
constants.NOT_EQUAL: "__ne__",
43+
constants.CONTAINS: "__contains__",
44+
}.get(self.operator)
45+
matching_function = getattr(
46+
trait_value, matching_function_name, lambda v: False
3147
)
32-
return evaluator_function(trait_value)
33-
34-
matching_function_name = {
35-
constants.EQUAL: "__eq__",
36-
constants.GREATER_THAN: "__gt__",
37-
constants.GREATER_THAN_INCLUSIVE: "__ge__",
38-
constants.LESS_THAN: "__lt__",
39-
constants.LESS_THAN_INCLUSIVE: "__le__",
40-
constants.NOT_EQUAL: "__ne__",
41-
constants.CONTAINS: "__contains__",
42-
}.get(self.operator)
43-
matching_function = getattr(
44-
trait_value, matching_function_name, lambda v: False
45-
)
46-
to_same_type_as_trait_value = get_casting_function(trait_value)
47-
return matching_function(to_same_type_as_trait_value(self.value))
48+
to_same_type_as_trait_value = get_casting_function(trait_value)
49+
return matching_function(to_same_type_as_trait_value(self.value))
50+
51+
return False
4852

4953
def evaluate_not_contains(self, trait_value: typing.Iterable) -> bool:
5054
return self.value not in trait_value

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name="flagsmith-flag-engine",
5-
version="2.0.7",
5+
version="2.1.0",
66
author="Flagsmith",
77
author_email="support@flagsmith.com",
88
packages=find_packages(include=["flag_engine", "flag_engine.*"]),

tests/mock_django_classes.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -204,20 +204,9 @@ def all(self) -> typing.List[DjangoFeatureState]:
204204

205205

206206
@dataclass
207-
class DjangoWebhook:
207+
class DjangoWebhookConfig:
208208
url: str
209209
secret: str
210-
created_at: datetime = field(default_factory=utcnow_with_tz)
211-
updated_at: datetime = field(default_factory=utcnow_with_tz)
212-
enabled: bool = True
213-
214-
215-
@dataclass
216-
class DjangoWebhookRelatedManager:
217-
webhooks: typing.List[DjangoWebhook]
218-
219-
def all(self) -> typing.List[DjangoWebhook]:
220-
return self.webhooks
221210

222211

223212
class DjangoEnvironment:
@@ -228,7 +217,7 @@ def __init__(
228217
name: str = "Test Environment",
229218
api_key: str = "api-key",
230219
feature_states: typing.List[DjangoFeatureState] = None,
231-
webhooks: typing.List[DjangoWebhook] = None,
220+
webhook_config: DjangoWebhookConfig = None,
232221
):
233222
if feature_states:
234223
assert not any(
@@ -240,7 +229,7 @@ def __init__(
240229
self.api_key = api_key
241230
self.project = project
242231
self.feature_states = DjangoFeatureStateRelatedManager(feature_states or [])
243-
self.webhooks = DjangoWebhookRelatedManager(webhooks or [])
232+
self.webhook_config = webhook_config
244233

245234

246235
@dataclass

tests/unit/api/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
DjangoSegmentCondition,
2121
DjangoSegmentRule,
2222
DjangoTrait,
23-
DjangoWebhook,
23+
DjangoWebhookConfig,
2424
)
2525

2626

@@ -95,7 +95,7 @@ def django_multivariate_feature_state(django_project):
9595

9696
@pytest.fixture
9797
def django_webhook():
98-
return DjangoWebhook(url="https://my.webhook.com/hook", secret="secret!")
98+
return DjangoWebhookConfig(url="https://my.webhook.com/hook", secret="secret!")
9999

100100

101101
@pytest.fixture()
@@ -118,7 +118,7 @@ def django_environment(
118118
django_multivariate_feature_state,
119119
django_enabled_feature_state_with_string_value,
120120
],
121-
webhooks=[django_webhook],
121+
webhook_config=django_webhook,
122122
)
123123

124124

tests/unit/api/test_document_builders.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,9 @@ def test_build_environment_document(
7777
)
7878
)
7979

80-
assert len(environment_document["webhooks"]) == 1
81-
serialized_webhook = environment_document["webhooks"][0]
82-
assert serialized_webhook["enabled"] == django_webhook.enabled
83-
assert serialized_webhook["url"] == django_webhook.url
84-
assert serialized_webhook["secret"] == django_webhook.secret
80+
assert environment_document["webhook_config"] is not None
81+
assert environment_document["webhook_config"]["url"] == django_webhook.url
82+
assert environment_document["webhook_config"]["secret"] == django_webhook.secret
8583

8684

8785
def test_build_environment_api_key_document(django_environment_api_key):

tests/unit/environments/test_environments_builders.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
def test_build_environment_model():
1515
"""Test to exercise the basic fields on the schema."""
1616
# Given
17+
webhook_url = "https://my.webhook.com/hook"
1718
environment_dict = {
1819
"id": 1,
1920
"api_key": "api-key",
@@ -37,15 +38,10 @@ def test_build_environment_model():
3738
"feature": {"id": 1, "name": "enabled_feature", "type": STANDARD},
3839
}
3940
],
40-
"webhooks": [
41-
{
42-
"url": "https://my.webhook.com/hook",
43-
"secret": "secret!",
44-
"created_at": "2022-05-11T11:33:14.073487+00:00",
45-
"updated_at": "2022-05-11T11:33:14.073487+00:00",
46-
"enabled": True,
47-
}
48-
],
41+
"webhook_config": {
42+
"url": webhook_url,
43+
"secret": "secret!",
44+
},
4945
}
5046

5147
# When
@@ -55,7 +51,7 @@ def test_build_environment_model():
5551
assert environment_model
5652

5753
assert len(environment_model.feature_states) == 1
58-
assert len(environment_model.webhooks) == 1
54+
assert environment_model.webhook_config.url == webhook_url
5955

6056

6157
def test_get_flags_for_environment_returns_feature_states_for_environment_dictionary():

tests/unit/segments/test_segments_models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
(constants.EQUAL, "bar", "bar", True),
1111
(constants.EQUAL, "bar", "baz", False),
1212
(constants.EQUAL, 1, "1", True),
13+
(constants.EQUAL, 1, "not_an_int", False),
1314
(constants.EQUAL, 1, "2", False),
1415
(constants.EQUAL, True, "True", True),
1516
(constants.EQUAL, False, "False", True),
1617
(constants.EQUAL, False, "True", False),
1718
(constants.EQUAL, True, "False", False),
1819
(constants.EQUAL, 1.23, "1.23", True),
20+
(constants.EQUAL, 1.23, "not_a_float", False),
1921
(constants.EQUAL, 1.23, "4.56", False),
2022
(constants.GREATER_THAN, 2, "1", True),
2123
(constants.GREATER_THAN, 1, "1", False),
@@ -75,6 +77,7 @@ def test_segment_condition_matches_trait_value(
7577
"operator, trait_value, condition_value, expected_result",
7678
[
7779
(constants.EQUAL, "1.0.0", "1.0.0:semver", True),
80+
(constants.EQUAL, "not_a_semver", "1.0.0:semver", False),
7881
(constants.EQUAL, "1.0.0", "1.0.1:semver", False),
7982
(constants.NOT_EQUAL, "1.0.0", "1.0.0:semver", False),
8083
(constants.NOT_EQUAL, "1.0.0", "1.0.1:semver", True),

0 commit comments

Comments
 (0)