diff --git a/pyproject.toml b/pyproject.toml index 71087452d5c44e..2430cb3c36673c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -198,7 +198,6 @@ module = [ "sentry.incidents.endpoints.bases", "sentry.incidents.endpoints.organization_alert_rule_details", "sentry.incidents.endpoints.organization_alert_rule_index", - "sentry.incidents.endpoints.organization_incident_comment_details", "sentry.incidents.endpoints.organization_incident_index", "sentry.incidents.subscription_processor", "sentry.incidents.tasks", diff --git a/src/sentry/api/urls.py b/src/sentry/api/urls.py index c1155a74b3502e..d631b249dc97e6 100644 --- a/src/sentry/api/urls.py +++ b/src/sentry/api/urls.py @@ -100,23 +100,10 @@ OrganizationAlertRuleIndexEndpoint, OrganizationCombinedRuleIndexEndpoint, ) -from sentry.incidents.endpoints.organization_incident_activity_index import ( - OrganizationIncidentActivityIndexEndpoint, -) -from sentry.incidents.endpoints.organization_incident_comment_details import ( - OrganizationIncidentCommentDetailsEndpoint, -) -from sentry.incidents.endpoints.organization_incident_comment_index import ( - OrganizationIncidentCommentIndexEndpoint, -) from sentry.incidents.endpoints.organization_incident_details import ( OrganizationIncidentDetailsEndpoint, ) from sentry.incidents.endpoints.organization_incident_index import OrganizationIncidentIndexEndpoint -from sentry.incidents.endpoints.organization_incident_seen import OrganizationIncidentSeenEndpoint -from sentry.incidents.endpoints.organization_incident_subscription_index import ( - OrganizationIncidentSubscriptionIndexEndpoint, -) from sentry.incidents.endpoints.project_alert_rule_details import ProjectAlertRuleDetailsEndpoint from sentry.incidents.endpoints.project_alert_rule_index import ProjectAlertRuleIndexEndpoint from sentry.incidents.endpoints.project_alert_rule_task_details import ( @@ -1206,21 +1193,6 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]: name="sentry-api-0-data-secrecy", ), # Incidents - re_path( - r"^(?P[^\/]+)/incidents/(?P[^\/]+)/activity/$", - OrganizationIncidentActivityIndexEndpoint.as_view(), - name="sentry-api-0-organization-incident-activity", - ), - re_path( - r"^(?P[^\/]+)/incidents/(?P[^\/]+)/comments/$", - OrganizationIncidentCommentIndexEndpoint.as_view(), - name="sentry-api-0-organization-incident-comments", - ), - re_path( - r"^(?P[^\/]+)/incidents/(?P[^\/]+)/comments/(?P[^\/]+)/$", - OrganizationIncidentCommentDetailsEndpoint.as_view(), - name="sentry-api-0-organization-incident-comment-details", - ), re_path( r"^(?P[^\/]+)/incidents/(?P[^\/]+)/$", OrganizationIncidentDetailsEndpoint.as_view(), @@ -1231,16 +1203,6 @@ def create_group_urls(name_prefix: str) -> list[URLPattern | URLResolver]: OrganizationIncidentIndexEndpoint.as_view(), name="sentry-api-0-organization-incident-index", ), - re_path( - r"^(?P[^\/]+)/incidents/(?P[^\/]+)/seen/$", - OrganizationIncidentSeenEndpoint.as_view(), - name="sentry-api-0-organization-incident-seen", - ), - re_path( - r"^(?P[^\/]+)/incidents/(?P[^\/]+)/subscriptions/$", - OrganizationIncidentSubscriptionIndexEndpoint.as_view(), - name="sentry-api-0-organization-incident-subscription-index", - ), re_path( r"^(?P[^\/]+)/chunk-upload/$", ChunkUploadEndpoint.as_view(), diff --git a/src/sentry/incidents/endpoints/organization_incident_activity_index.py b/src/sentry/incidents/endpoints/organization_incident_activity_index.py deleted file mode 100644 index 0437f5fa249a03..00000000000000 --- a/src/sentry/incidents/endpoints/organization_incident_activity_index.py +++ /dev/null @@ -1,33 +0,0 @@ -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import region_silo_endpoint -from sentry.api.bases.incident import IncidentEndpoint, IncidentPermission -from sentry.api.paginator import OffsetPaginator -from sentry.api.serializers import serialize -from sentry.incidents.logic import get_incident_activity - - -@region_silo_endpoint -class OrganizationIncidentActivityIndexEndpoint(IncidentEndpoint): - owner = ApiOwner.ISSUES - publish_status = { - "GET": ApiPublishStatus.UNKNOWN, - } - permission_classes = (IncidentPermission,) - - def get(self, request: Request, organization, incident) -> Response: - if request.GET.get("desc", "1") == "1": - order_by = "-date_added" - else: - order_by = "date_added" - - return self.paginate( - request=request, - queryset=get_incident_activity(incident), - order_by=order_by, - paginator_cls=OffsetPaginator, - on_results=lambda x: serialize(x, request.user), - ) diff --git a/src/sentry/incidents/endpoints/organization_incident_comment_details.py b/src/sentry/incidents/endpoints/organization_incident_comment_details.py deleted file mode 100644 index fdfc47e8a31cfb..00000000000000 --- a/src/sentry/incidents/endpoints/organization_incident_comment_details.py +++ /dev/null @@ -1,88 +0,0 @@ -from rest_framework import serializers -from rest_framework.exceptions import PermissionDenied -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import region_silo_endpoint -from sentry.api.bases.incident import IncidentEndpoint, IncidentPermission -from sentry.api.exceptions import ResourceDoesNotExist -from sentry.api.serializers import serialize -from sentry.incidents.models.incident import IncidentActivity, IncidentActivityType - - -class CommentSerializer(serializers.Serializer): - comment = serializers.CharField(required=True) - - -class CommentDetailsEndpoint(IncidentEndpoint): - def convert_args(self, request: Request, activity_id, *args, **kwargs): - # See GroupNotesDetailsEndpoint: - # We explicitly don't allow a request with an ApiKey - # since an ApiKey is bound to the Organization, not - # an individual. Not sure if we'd want to allow an ApiKey - # to delete/update other users' comments - if not request.user.is_authenticated: - raise PermissionDenied(detail="Key doesn't have permission to delete Note") - - args, kwargs = super().convert_args(request, *args, **kwargs) - - try: - # Superusers may mutate any comment - user_filter = {} if request.user.is_superuser else {"user_id": request.user.id} - - kwargs["activity"] = IncidentActivity.objects.get( - id=activity_id, - incident=kwargs["incident"], - # Only allow modifying comments - type=IncidentActivityType.COMMENT.value, - **user_filter, - ) - except IncidentActivity.DoesNotExist: - raise ResourceDoesNotExist - - return args, kwargs - - -@region_silo_endpoint -class OrganizationIncidentCommentDetailsEndpoint(CommentDetailsEndpoint): - owner = ApiOwner.ISSUES - publish_status = { - "DELETE": ApiPublishStatus.UNKNOWN, - "PUT": ApiPublishStatus.UNKNOWN, - } - permission_classes = (IncidentPermission,) - - def delete(self, request: Request, organization, incident, activity) -> Response: - """ - Delete a comment - ```````````````` - :auth: required - """ - - try: - activity.delete() - except IncidentActivity.DoesNotExist: - raise ResourceDoesNotExist - - return Response(status=204) - - def put(self, request: Request, organization, incident, activity) -> Response: - """ - Update an existing comment - `````````````````````````` - :auth: required - """ - - serializer = CommentSerializer(data=request.data) - if serializer.is_valid(): - result = serializer.validated_data - - try: - comment = activity.update(comment=result.get("comment")) - except IncidentActivity.DoesNotExist: - raise ResourceDoesNotExist - - return Response(serialize(comment, request.user), status=200) - return Response(serializer.errors, status=400) diff --git a/src/sentry/incidents/endpoints/organization_incident_comment_index.py b/src/sentry/incidents/endpoints/organization_incident_comment_index.py deleted file mode 100644 index b2a534ed598b23..00000000000000 --- a/src/sentry/incidents/endpoints/organization_incident_comment_index.py +++ /dev/null @@ -1,56 +0,0 @@ -from rest_framework import serializers -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import region_silo_endpoint -from sentry.api.bases.incident import IncidentEndpoint, IncidentPermission -from sentry.api.fields.actor import ActorField -from sentry.api.serializers import serialize -from sentry.api.serializers.rest_framework.mentions import ( - MentionsMixin, - extract_user_ids_from_mentions, -) -from sentry.incidents.logic import create_incident_activity -from sentry.incidents.models.incident import IncidentActivityType -from sentry.users.services.user.serial import serialize_generic_user - - -class CommentSerializer(serializers.Serializer, MentionsMixin): - comment = serializers.CharField(required=True) - mentions = serializers.ListField(child=ActorField(), required=False) - external_id = serializers.CharField(allow_null=True, required=False) - - -@region_silo_endpoint -class OrganizationIncidentCommentIndexEndpoint(IncidentEndpoint): - owner = ApiOwner.ISSUES - publish_status = { - "POST": ApiPublishStatus.UNKNOWN, - } - permission_classes = (IncidentPermission,) - - def post(self, request: Request, organization, incident) -> Response: - serializer = CommentSerializer( - data=request.data, - context={ - "projects": incident.projects.all(), - "organization": organization, - "organization_id": organization.id, - }, - ) - if serializer.is_valid(): - mentions = extract_user_ids_from_mentions( - organization.id, serializer.validated_data.get("mentions", []) - ) - mentioned_user_ids = mentions["users"] | mentions["team_users"] - activity = create_incident_activity( - incident, - IncidentActivityType.COMMENT, - user=serialize_generic_user(request.user), - comment=serializer.validated_data["comment"], - mentioned_user_ids=mentioned_user_ids, - ) - return Response(serialize(activity, request.user), status=201) - return Response(serializer.errors, status=400) diff --git a/src/sentry/incidents/endpoints/organization_incident_seen.py b/src/sentry/incidents/endpoints/organization_incident_seen.py deleted file mode 100644 index 869596a422b021..00000000000000 --- a/src/sentry/incidents/endpoints/organization_incident_seen.py +++ /dev/null @@ -1,53 +0,0 @@ -from django.utils import timezone as django_timezone -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import region_silo_endpoint -from sentry.api.bases.incident import IncidentEndpoint, IncidentPermission -from sentry.incidents.models.incident import Incident, IncidentProject, IncidentSeen -from sentry.models.organization import Organization -from sentry.users.services.user import RpcUser -from sentry.users.services.user.serial import serialize_generic_user - - -@region_silo_endpoint -class OrganizationIncidentSeenEndpoint(IncidentEndpoint): - owner = ApiOwner.ISSUES - publish_status = { - "POST": ApiPublishStatus.UNKNOWN, - } - permission_classes = (IncidentPermission,) - - def post(self, request: Request, organization: Organization, incident: Incident) -> Response: - """ - Mark an incident as seen by the user - ```````````````````````````````````` - - :auth: required - """ - - user = serialize_generic_user(request.user) - if user is not None: - _set_incident_seen(incident, user) - return Response({}, status=201) - - -def _set_incident_seen(incident: Incident, user: RpcUser) -> None: - """ - Updates the incident to be seen - """ - - def is_project_member() -> bool: - incident_projects = IncidentProject.objects.filter(incident=incident) - for incident_project in incident_projects.select_related("project"): - if incident_project.project.member_set.filter(user_id=user.id).exists(): - return True - return False - - is_org_member = incident.organization.has_access(user) - if is_org_member and is_project_member(): - IncidentSeen.objects.create_or_update( - incident=incident, user_id=user.id, values={"last_seen": django_timezone.now()} - ) diff --git a/src/sentry/incidents/endpoints/organization_incident_subscription_index.py b/src/sentry/incidents/endpoints/organization_incident_subscription_index.py deleted file mode 100644 index dd22eea9d5a32e..00000000000000 --- a/src/sentry/incidents/endpoints/organization_incident_subscription_index.py +++ /dev/null @@ -1,52 +0,0 @@ -from rest_framework.request import Request -from rest_framework.response import Response - -from sentry.api.api_owners import ApiOwner -from sentry.api.api_publish_status import ApiPublishStatus -from sentry.api.base import region_silo_endpoint -from sentry.api.bases.incident import IncidentEndpoint, IncidentPermission -from sentry.incidents.logic import subscribe_to_incident, unsubscribe_from_incident - - -class IncidentSubscriptionPermission(IncidentPermission): - scope_map = IncidentPermission.scope_map.copy() - scope_map["DELETE"] = [ - "org:write", - "org:admin", - "project:read", - "project:write", - "project:admin", - ] - - -@region_silo_endpoint -class OrganizationIncidentSubscriptionIndexEndpoint(IncidentEndpoint): - owner = ApiOwner.ISSUES - publish_status = { - "DELETE": ApiPublishStatus.UNKNOWN, - "POST": ApiPublishStatus.UNKNOWN, - } - permission_classes = (IncidentSubscriptionPermission,) - - def post(self, request: Request, organization, incident) -> Response: - """ - Subscribes the authenticated user to the incident. - `````````````````````````````````````````````````` - Subscribes the user to the incident. If they are already subscribed - then no-op. - :auth: required - """ - - subscribe_to_incident(incident, request.user.id) - return Response({}, status=201) - - def delete(self, request: Request, organization, incident) -> Response: - """ - Unsubscribes the authenticated user from the incident. - `````````````````````````````````````````````````````` - Unsubscribes the user from the incident. If they are not subscribed then - no-op. - :auth: required - """ - unsubscribe_from_incident(incident, request.user.id) - return Response({}, status=200) diff --git a/tests/sentry/incidents/endpoints/test_organization_incident_activity_index.py b/tests/sentry/incidents/endpoints/test_organization_incident_activity_index.py deleted file mode 100644 index 01a2b9b33d5f0d..00000000000000 --- a/tests/sentry/incidents/endpoints/test_organization_incident_activity_index.py +++ /dev/null @@ -1,75 +0,0 @@ -from datetime import timedelta -from functools import cached_property - -from django.utils import timezone - -from sentry.api.serializers import serialize -from sentry.incidents.logic import create_incident_activity -from sentry.incidents.models.incident import IncidentActivityType -from sentry.testutils.cases import APITestCase - - -class OrganizationIncidentActivityIndexTest(APITestCase): - endpoint = "sentry-api-0-organization-incident-activity" - - def setUp(self): - self.create_team(organization=self.organization, members=[self.user]) - self.login_as(self.user) - - @cached_property - def organization(self): - return self.create_organization(owner=self.create_user()) - - @cached_property - def project(self): - return self.create_project(organization=self.organization) - - @cached_property - def user(self): - return self.create_user() - - def test_no_perms(self): - incident = self.create_incident() - self.login_as(self.create_user()) - with self.feature("organizations:incidents"): - resp = self.get_response(incident.organization.slug, incident.id) - assert resp.status_code == 403 - - def test_no_feature(self): - incident = self.create_incident() - resp = self.get_response(incident.organization.slug, incident.id) - assert resp.status_code == 404 - - def test_simple(self): - incident = self.create_incident( - date_started=timezone.now() - timedelta(hours=2), projects=[self.project], query="" - ) - activities = [ - create_incident_activity( - incident=incident, - activity_type=IncidentActivityType.CREATED, - user=self.user, - comment="hello", - ), - create_incident_activity( - incident=incident, - activity_type=IncidentActivityType.COMMENT, - user=self.user, - comment="goodbye", - ), - ] - - expected = serialize(activities, user=self.user) - - with self.feature("organizations:incidents"): - resp = self.get_success_response( - incident.organization.slug, incident.identifier, desc=0 - ) - - assert resp.data == expected - - expected.reverse() - with self.feature("organizations:incidents"): - resp = self.get_success_response(incident.organization.slug, incident.identifier) - - assert resp.data == expected diff --git a/tests/sentry/incidents/endpoints/test_organization_incident_comment_details.py b/tests/sentry/incidents/endpoints/test_organization_incident_comment_details.py deleted file mode 100644 index f03ec7ff51563e..00000000000000 --- a/tests/sentry/incidents/endpoints/test_organization_incident_comment_details.py +++ /dev/null @@ -1,146 +0,0 @@ -from functools import cached_property - -from sentry.incidents.models.incident import IncidentActivity, IncidentActivityType -from sentry.silo.base import SiloMode -from sentry.testutils.cases import APITestCase -from sentry.testutils.silo import assume_test_silo_mode - - -class BaseIncidentCommentDetailsTest(APITestCase): - method = "put" - endpoint = "sentry-api-0-organization-incident-comment-details" - - def setUp(self): - self.create_member( - user=self.user, organization=self.organization, role="owner", teams=[self.team] - ) - self.login_as(self.user) - self.activity = self.create_incident_comment(self.incident, user_id=self.user.id) - self.detected_activity = self.create_incident_activity( - self.incident, user_id=self.user.id, type=IncidentActivityType.CREATED.value - ) - - user2 = self.create_user() - self.user2_activity = self.create_incident_comment( - incident=self.incident, user_id=user2.id, comment="hello from another user" - ) - - @cached_property - def organization(self): - return self.create_organization() - - @cached_property - def project(self): - return self.create_project(organization=self.organization) - - @cached_property - def user(self): - return self.create_user() - - @cached_property - def incident(self): - return self.create_incident() - - def test_not_found(self): - comment = "hello" - with self.feature("organizations:incidents"): - self.get_error_response( - self.organization.slug, - self.incident.identifier, - 123, - comment=comment, - status_code=404, - ) - - def test_non_comment_type(self): - comment = "hello" - with self.feature("organizations:incidents"): - self.get_error_response( - self.organization.slug, - self.incident.identifier, - self.detected_activity.id, - comment=comment, - status_code=404, - ) - - -class OrganizationIncidentCommentUpdateEndpointTest(BaseIncidentCommentDetailsTest): - method = "put" - - def test_simple(self): - comment = "hello" - with self.feature("organizations:incidents"): - self.get_success_response( - self.organization.slug, - self.incident.identifier, - self.activity.id, - comment=comment, - status_code=200, - ) - activity = IncidentActivity.objects.get(id=self.activity.id) - assert activity.type == IncidentActivityType.COMMENT.value - assert activity.user_id == self.user.id - assert activity.comment == comment - - def test_cannot_edit_others_comment(self): - with self.feature("organizations:incidents"): - self.get_error_response( - self.organization.slug, - self.incident.identifier, - self.user2_activity.id, - comment="edited comment", - status_code=404, - ) - - def test_superuser_can_edit(self): - self.user.is_superuser = True - with assume_test_silo_mode(SiloMode.CONTROL): - self.user.save() - - edited_comment = "this comment has been edited" - - with self.feature("organizations:incidents"): - self.get_success_response( - self.organization.slug, - self.incident.identifier, - self.user2_activity.id, - comment=edited_comment, - status_code=200, - ) - activity = IncidentActivity.objects.get(id=self.user2_activity.id) - assert activity.user_id != self.user.id - assert activity.comment == edited_comment - - -class OrganizationIncidentCommentDeleteEndpointTest(BaseIncidentCommentDetailsTest): - method = "delete" - - def test_simple(self): - with self.feature("organizations:incidents"): - self.get_success_response( - self.organization.slug, self.incident.identifier, self.activity.id, status_code=204 - ) - assert not IncidentActivity.objects.filter(id=self.activity.id).exists() - - def test_cannot_delete_others_comments(self): - with self.feature("organizations:incidents"): - self.get_error_response( - self.organization.slug, - self.incident.identifier, - self.user2_activity.id, - status_code=404, - ) - - def test_superuser_can_delete(self): - self.user.is_superuser = True - with assume_test_silo_mode(SiloMode.CONTROL): - self.user.save() - - with self.feature("organizations:incidents"): - self.get_success_response( - self.organization.slug, - self.incident.identifier, - self.user2_activity.id, - status_code=204, - ) - assert not IncidentActivity.objects.filter(id=self.user2_activity.id).exists() diff --git a/tests/sentry/incidents/endpoints/test_organization_incident_comment_index.py b/tests/sentry/incidents/endpoints/test_organization_incident_comment_index.py deleted file mode 100644 index 6837a56bfd4d85..00000000000000 --- a/tests/sentry/incidents/endpoints/test_organization_incident_comment_index.py +++ /dev/null @@ -1,84 +0,0 @@ -from functools import cached_property - -from sentry.api.serializers import serialize -from sentry.incidents.models.incident import ( - IncidentActivity, - IncidentActivityType, - IncidentSubscription, -) -from sentry.testutils.cases import APITestCase - - -class OrganizationIncidentCommentCreateEndpointTest(APITestCase): - endpoint = "sentry-api-0-organization-incident-comments" - method = "post" - - @cached_property - def organization(self): - return self.create_organization() - - @cached_property - def project(self): - return self.create_project(organization=self.organization) - - @cached_property - def user(self): - return self.create_user() - - def test_simple(self): - self.create_member( - user=self.user, organization=self.organization, role="owner", teams=[self.team] - ) - self.login_as(self.user) - comment = "hello" - incident = self.create_incident() - with self.feature("organizations:incidents"): - resp = self.get_success_response( - self.organization.slug, incident.identifier, comment=comment, status_code=201 - ) - activity = IncidentActivity.objects.get(id=resp.data["id"]) - assert activity.type == IncidentActivityType.COMMENT.value - assert activity.user_id == self.user.id - assert activity.comment == comment - assert resp.data == serialize([activity], self.user)[0] - - def test_mentions(self): - self.create_member( - user=self.user, organization=self.organization, role="owner", teams=[self.team] - ) - mentioned_member = self.create_user() - self.create_member( - user=mentioned_member, organization=self.organization, role="owner", teams=[self.team] - ) - self.login_as(self.user) - comment = "hello **@%s**" % mentioned_member.username - incident = self.create_incident() - with self.feature("organizations:incidents"): - resp = self.get_success_response( - self.organization.slug, - incident.identifier, - comment=comment, - mentions=["user:%s" % mentioned_member.id], - status_code=201, - ) - activity = IncidentActivity.objects.get(id=resp.data["id"]) - assert activity.type == IncidentActivityType.COMMENT.value - assert activity.user_id == self.user.id - assert activity.comment == comment - assert resp.data == serialize([activity], self.user)[0] - assert IncidentSubscription.objects.filter( - user_id=mentioned_member.id, incident=incident - ).exists() - - def test_access(self): - other_user = self.create_user() - self.login_as(other_user) - other_team = self.create_team() - self.create_member( - user=self.user, organization=self.organization, role="member", teams=[self.team] - ) - other_project = self.create_project(teams=[other_team]) - incident = self.create_incident(projects=[other_project]) - with self.feature("organizations:incidents"): - resp = self.get_response(self.organization.slug, incident.identifier, comment="hi") - assert resp.status_code == 403 diff --git a/tests/sentry/incidents/endpoints/test_organization_incident_seen.py b/tests/sentry/incidents/endpoints/test_organization_incident_seen.py deleted file mode 100644 index 323a2523461a72..00000000000000 --- a/tests/sentry/incidents/endpoints/test_organization_incident_seen.py +++ /dev/null @@ -1,69 +0,0 @@ -from functools import cached_property - -from django.urls import reverse - -from sentry.incidents.models.incident import IncidentSeen -from sentry.testutils.cases import APITestCase - - -class OrganizationIncidentSeenTest(APITestCase): - method = "post" - endpoint = "sentry-api-0-organization-incident-seen" - - def setUp(self): - self.create_team(organization=self.organization, members=[self.user]) - self.login_as(self.user) - - @cached_property - def organization(self): - return self.create_organization(owner=self.create_user()) - - @cached_property - def project(self): - return self.create_project(organization=self.organization) - - @cached_property - def user(self): - return self.create_user() - - def test_has_user_seen(self): - incident = self.create_incident() - with self.feature("organizations:incidents"): - resp = self.get_response(incident.organization.slug, incident.identifier) - - assert resp.status_code == 201 - - # should not be seen by different user - new_user = self.create_user() - self.create_member(user=new_user, organization=self.organization, teams=[self.team]) - self.login_as(new_user) - - seen_incidents = IncidentSeen.objects.filter(incident=incident) - assert len(seen_incidents) == 1 - assert seen_incidents[0].user_id == self.user.id - - # mark set as seen by new_user - resp = self.get_response(incident.organization.slug, incident.identifier) - assert resp.status_code == 201 - - seen_incidents = IncidentSeen.objects.filter(incident=incident) - assert len(seen_incidents) == 2 - assert seen_incidents[0].user_id == self.user.id - assert seen_incidents[1].user_id == new_user.id - - url = reverse( - "sentry-api-0-organization-incident-details", - kwargs={ - "organization_id_or_slug": incident.organization.slug, - "incident_identifier": incident.identifier, - }, - ) - - resp = self.client.get(url, format="json") - assert resp.status_code == 200 - assert resp.data["hasSeen"] - - assert len(resp.data["seenBy"]) == 2 - # seenBy is sorted by most recently seen - assert resp.data["seenBy"][0]["username"] == new_user.username - assert resp.data["seenBy"][1]["username"] == self.user.username diff --git a/tests/sentry/incidents/endpoints/test_organization_incident_subscription_index.py b/tests/sentry/incidents/endpoints/test_organization_incident_subscription_index.py deleted file mode 100644 index 35df013a3c84b5..00000000000000 --- a/tests/sentry/incidents/endpoints/test_organization_incident_subscription_index.py +++ /dev/null @@ -1,70 +0,0 @@ -from functools import cached_property - -from sentry.incidents.logic import subscribe_to_incident -from sentry.incidents.models.incident import IncidentSubscription -from sentry.testutils.abstract import Abstract -from sentry.testutils.cases import APITestCase - - -class BaseOrganizationSubscriptionEndpointTest(APITestCase): - __test__ = Abstract(__module__, __qualname__) - - endpoint = "sentry-api-0-organization-incident-subscription-index" - - @cached_property - def organization(self): - return self.create_organization() - - @cached_property - def project(self): - return self.create_project(organization=self.organization) - - @cached_property - def user(self): - return self.create_user() - - def test_access(self): - other_user = self.create_user() - self.login_as(other_user) - other_team = self.create_team() - self.create_member( - user=self.user, organization=self.organization, role="member", teams=[self.team] - ) - other_project = self.create_project(teams=[other_team]) - incident = self.create_incident(projects=[other_project]) - with self.feature("organizations:incidents"): - resp = self.get_response(self.organization.slug, incident.identifier, comment="hi") - assert resp.status_code == 403 - - -class OrganizationIncidentSubscribeEndpointTest(BaseOrganizationSubscriptionEndpointTest): - method = "post" - - def test_simple(self): - self.create_member( - user=self.user, organization=self.organization, role="owner", teams=[self.team] - ) - self.login_as(self.user) - incident = self.create_incident() - with self.feature("organizations:incidents"): - self.get_success_response(self.organization.slug, incident.identifier, status_code=201) - sub = IncidentSubscription.objects.filter(incident=incident, user_id=self.user.id).get() - assert sub.incident == incident - assert sub.user_id == self.user.id - - -class OrganizationIncidentUnsubscribeEndpointTest(BaseOrganizationSubscriptionEndpointTest): - method = "delete" - - def test_simple(self): - self.create_member( - user=self.user, organization=self.organization, role="owner", teams=[self.team] - ) - self.login_as(self.user) - incident = self.create_incident() - subscribe_to_incident(incident, self.user.id) - with self.feature("organizations:incidents"): - self.get_success_response(self.organization.slug, incident.identifier, status_code=200) - assert not IncidentSubscription.objects.filter( - incident=incident, user_id=self.user.id - ).exists()