-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add metadata fields to core entities (FE) #3212
Changes from 42 commits
f41c8d5
e1bc9ec
930c9c8
625e331
2b0b9d5
2293935
830a82a
c1a0a7a
95033ed
1a0c84a
db8f13a
a9c0490
2eac883
dc4d2a2
74db03d
9f9b283
5c65852
6f6512e
0b6c89b
9a25d76
8b08a3e
557f68b
cc2bada
c52cb3b
14e0f92
37e79e6
5536f45
8a489a2
9ddf027
aee6657
2e19984
3057e7c
6f4871a
a12ffa3
429c673
0275c09
9844cd5
8763b09
dc60ee4
28a5617
0dc05e9
1ec1125
d06f518
6cbd20c
3217c37
cef4d4e
264bb0d
df39f39
0643314
8bee370
c743a6f
b56f8bd
5d0c952
f0bfc37
f1a4bb0
131e847
0c4d817
3ab272b
b342d58
652a9d0
67e474f
e4fec07
30af6f6
3a6864c
3341574
fba77d8
6282e28
661ecea
2006f79
dfb7ea4
c7f1f06
98efcd7
e0036c4
85da11c
f224fdd
18b30b5
77b33ac
1677ac5
8a87411
b44b863
7453a51
08d9797
d87dff4
66ba1d6
a2b2f2f
25614d2
f8f7943
28872d6
b87c99d
71d5027
f62bdfc
a2a95e1
2de5489
e985331
2be4a6f
08318c4
3c70da6
c0b1f3f
9e02761
d32bba7
1c9d52c
e3ee0b5
3e4392e
ad2363d
02af18a
3e055c2
2a70966
71ea34d
4be8797
6535df8
4824e33
ea652de
011db90
6f40645
b395e7e
e883981
fc96a29
ca4d00d
086cf75
0bcd0fe
2d7c8ca
29a11df
b56b012
ac7cfa8
f971775
fc965d2
7145d96
e2d0418
e90d205
8377d38
b145be7
61f2c5f
6e5f0ed
e46d285
5501e4b
17124db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ | |
FeatureInfluxDataSerializer, | ||
FeatureOwnerInputSerializer, | ||
FeatureQuerySerializer, | ||
FeatureSerializerWithMetadata, | ||
FeatureStateSerializerBasic, | ||
FeatureStateSerializerCreate, | ||
FeatureStateSerializerFull, | ||
|
@@ -103,9 +104,9 @@ class FeatureViewSet(viewsets.ModelViewSet): | |
|
||
def get_serializer_class(self): | ||
return { | ||
"list": ListCreateFeatureSerializer, | ||
"retrieve": ListCreateFeatureSerializer, | ||
"create": ListCreateFeatureSerializer, | ||
"list": FeatureSerializerWithMetadata, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the metadata on the list endpoint? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understanding how it was working in the environments, yes. |
||
"retrieve": FeatureSerializerWithMetadata, | ||
"create": FeatureSerializerWithMetadata, | ||
"update": UpdateFeatureSerializer, | ||
"partial_update": UpdateFeatureSerializer, | ||
}.get(self.action, ProjectFeatureSerializer) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
import typing | ||
|
||
from django.contrib.contenttypes.models import ContentType | ||
from flag_engine.segments.constants import PERCENTAGE_SPLIT | ||
from rest_framework import serializers | ||
from rest_framework.exceptions import ValidationError | ||
from rest_framework.serializers import ListSerializer | ||
from rest_framework_recursive.fields import RecursiveField | ||
|
||
from metadata.models import Metadata | ||
from metadata.serializers import MetadataSerializer | ||
from projects.models import Project | ||
from segments.models import Condition, Segment, SegmentRule | ||
|
||
|
@@ -41,6 +44,7 @@ class Meta: | |
|
||
class SegmentSerializer(serializers.ModelSerializer): | ||
rules = RuleSerializer(many=True) | ||
metadata = MetadataSerializer(required=False, many=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm keen for this not to be included on any serializers that are used for list views, which I think this is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this was answered here: #3315 |
||
|
||
class Meta: | ||
model = Segment | ||
|
@@ -58,12 +62,14 @@ def create(self, validated_data): | |
self.validate_project_segment_limit(project) | ||
|
||
rules_data = validated_data.pop("rules", []) | ||
metadata_data = validated_data.pop("metadata", []) | ||
|
||
# create segment with nested rules and conditions | ||
segment = Segment.objects.create(**validated_data) | ||
self._update_or_create_segment_rules( | ||
rules_data, segment=segment, is_create=True | ||
) | ||
self._update_or_create_metadata(metadata_data, segment=segment) | ||
return segment | ||
|
||
def validate_project_segment_limit(self, project: Project) -> None: | ||
|
@@ -120,6 +126,22 @@ def _update_or_create_segment_rules( | |
child_rules, rule=child_rule, is_create=is_create | ||
) | ||
|
||
def _update_or_create_metadata(self, metadata_data, segment=None): | ||
for metadata_item in metadata_data: | ||
metadata_id = metadata_item.pop("id", None) | ||
if metadata_item.get("delete"): | ||
Metadata.objects.filter(id=metadata_id).delete() | ||
continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit confused here - I don't think this is how it works for other entities? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this was answered here: #3315 |
||
|
||
Metadata.objects.update_or_create( | ||
id=metadata_id, | ||
defaults={ | ||
**metadata_item, | ||
"content_type": ContentType.objects.get_for_model(Segment), | ||
"object_id": segment.id, | ||
}, | ||
) | ||
|
||
@staticmethod | ||
def _update_or_create_segment_rule( | ||
rule_data: dict, segment: Segment = None, rule: SegmentRule = None | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2219,6 +2219,42 @@ def test_cannot_update_feature_of_a_feature_state( | |
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"client", | ||
[lazy_fixture("admin_master_api_key_client"), lazy_fixture("admin_client")], | ||
) | ||
def test_create_feature_with_required_metadata_returns_201( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about other tests? e.g. trying to create a feature without the required metadata? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added |
||
project, | ||
client, | ||
required_a_feature_metadata_field, | ||
): | ||
# Given | ||
url = reverse("api-v1:projects:project-features-list", args=[project.id]) | ||
description = "This is the description" | ||
field_value = 10 | ||
data = { | ||
"name": "Test feature", | ||
"description": description, | ||
"metadata": [ | ||
{ | ||
"model_field": required_a_feature_metadata_field.id, | ||
"field_value": field_value, | ||
}, | ||
], | ||
} | ||
|
||
# When | ||
response = client.post(url, data=json.dumps(data), content_type="application/json") | ||
|
||
# Then | ||
assert response.status_code == status.HTTP_201_CREATED | ||
assert ( | ||
response.json()["metadata"][0]["model_field"] | ||
== required_a_feature_metadata_field.field.id | ||
) | ||
assert response.json()["metadata"][0]["field_value"] == str(field_value) | ||
|
||
|
||
def test_create_segment_override__using_simple_feature_state_viewset__allows_manage_segment_overrides( | ||
staff_client: APIClient, | ||
with_environment_permissions: Callable[ | ||
|
@@ -2399,7 +2435,7 @@ def test_list_features_n_plus_1( | |
v1_feature_state.clone(env=environment, version=i, live_from=timezone.now()) | ||
|
||
# When | ||
with django_assert_num_queries(14): | ||
with django_assert_num_queries(15): | ||
response = staff_client.get(url) | ||
|
||
# Then | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected