Skip to content

Commit b905af2

Browse files
authored
fix(Segments): Fix project reference in segment creation (#6929)
1 parent 1c67f96 commit b905af2

File tree

3 files changed

+43
-11
lines changed

3 files changed

+43
-11
lines changed

api/segments/serializers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,14 @@ class Meta:
115115

116116
def validate(self, attrs: dict[str, Any]) -> dict[str, Any]:
117117
attrs = super().validate(attrs)
118-
project = self.instance.project if self.instance else attrs["project"] # type: ignore[union-attr]
118+
metadata = attrs.get("metadata", [])
119+
120+
# TODO: Make "project" read-only — https://github.com/Flagsmith/flagsmith-workflows/issues/102
121+
project_pk = self.context["view"].kwargs["project_pk"]
122+
project = attrs["project"] = Project.objects.get(pk=project_pk)
119123
organisation = project.organisation
120124

121-
self._validate_required_metadata(
122-
organisation, attrs.get("metadata", []), project=project
123-
)
125+
self._validate_required_metadata(organisation, metadata, project)
124126
self._validate_segment_rules_conditions_limit(attrs["rules"])
125127
self._validate_project_segment_limit(project)
126128
return attrs

api/segments/views.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Any
2+
from typing import TYPE_CHECKING, Any
33

44
from common.projects.permissions import VIEW_PROJECT
55
from django.utils.decorators import method_decorator
@@ -21,6 +21,7 @@
2121
SegmentAssociatedFeatureStateSerializer,
2222
)
2323
from features.versioning.models import EnvironmentFeatureVersion
24+
from projects.models import Project
2425

2526
from .models import Segment
2627
from .permissions import SegmentPermissions
@@ -31,6 +32,9 @@
3132
)
3233
from .services import delete_segment
3334

35+
if TYPE_CHECKING:
36+
from users.models import FFAdminUser
37+
3438
logger = logging.getLogger()
3539

3640

@@ -88,15 +92,16 @@ class SegmentViewSet(viewsets.ModelViewSet): # type: ignore[type-arg]
8892
permission_classes = [SegmentPermissions]
8993
pagination_class = CustomPagination
9094

95+
def get_project(self) -> Project:
96+
user: "FFAdminUser" = self.request.user # type: ignore[assignment]
97+
projects = user.get_permitted_projects(permission_key=VIEW_PROJECT)
98+
return get_object_or_404(projects, pk=self.kwargs["project_pk"])
99+
91100
def get_queryset(self): # type: ignore[no-untyped-def]
92101
if getattr(self, "swagger_fake_view", False):
93102
return Segment.objects.none()
94103

95-
permitted_projects = self.request.user.get_permitted_projects( # type: ignore[union-attr]
96-
permission_key=VIEW_PROJECT
97-
)
98-
project = get_object_or_404(permitted_projects, pk=self.kwargs["project_pk"])
99-
104+
project = self.get_project()
100105
queryset = Segment.live_objects.filter(project=project, is_system_segment=False)
101106

102107
if self.action == "list":

api/tests/unit/segments/test_unit_segments_views.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def test_segments_limit_ignores_old_segment_versions(
205205
with_project_permissions: WithProjectPermissionsCallable,
206206
) -> None:
207207
# Given
208-
with_project_permissions([MANAGE_SEGMENTS]) # type: ignore[call-arg]
208+
with_project_permissions([MANAGE_SEGMENTS, VIEW_PROJECT]) # type: ignore[call-arg]
209209

210210
# let's reduce the max segments allowed to 2
211211
project.max_segments_allowed = 2
@@ -1884,3 +1884,28 @@ def test_create_segment__required_metadata_on_other_project__returns_201(
18841884

18851885
# Then
18861886
assert response.status_code == status.HTTP_201_CREATED
1887+
1888+
1889+
def test_create_segment__body_project_differs_from_url__does_not_create_in_other_project(
1890+
admin_client: APIClient,
1891+
project: Project,
1892+
) -> None:
1893+
# Given
1894+
other_org = Organisation.objects.create(name="Other Org")
1895+
other_project = Project.objects.create(name="Other Project", organisation=other_org)
1896+
1897+
# When
1898+
response = admin_client.post(
1899+
f"/api/v1/projects/{project.id}/segments/",
1900+
data={
1901+
"name": "a_wild_pokemon",
1902+
"project": other_project.id,
1903+
"rules": [{"type": "ALL", "rules": [], "conditions": []}],
1904+
},
1905+
format="json",
1906+
)
1907+
1908+
# Then
1909+
assert response.status_code == status.HTTP_201_CREATED
1910+
assert response.json()["project"] == project.id
1911+
assert not Segment.objects.filter(project=other_project).exists()

0 commit comments

Comments
 (0)