views.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. from django.shortcuts import get_object_or_404
  2. from rest_framework import exceptions, mixins, status, viewsets
  3. from rest_framework.decorators import action
  4. from rest_framework.filters import OrderingFilter
  5. from rest_framework.response import Response
  6. from organizations_ext.models import Organization, OrganizationUserRole
  7. from teams.models import Team
  8. from teams.views import NestedTeamViewSet
  9. from .models import Project, ProjectKey
  10. from .permissions import ProjectKeyPermission, ProjectPermission
  11. from .serializers.serializers import (
  12. BaseProjectSerializer,
  13. OrganizationProjectSerializer,
  14. ProjectDetailSerializer,
  15. ProjectKeySerializer,
  16. ProjectSerializer,
  17. )
  18. class BaseProjectViewSet(viewsets.ReadOnlyModelViewSet):
  19. serializer_class = BaseProjectSerializer
  20. queryset = Project.objects.all()
  21. lookup_field = "slug"
  22. permission_classes = [ProjectPermission]
  23. def get_object(self):
  24. queryset = self.filter_queryset(self.get_queryset())
  25. slug = self.kwargs.get("project_slug", self.kwargs.get("slug"))
  26. obj = get_object_or_404(
  27. queryset,
  28. slug=slug,
  29. organization__slug=self.kwargs["organization_slug"],
  30. )
  31. self.check_object_permissions(self.request, obj)
  32. return obj
  33. def get_queryset(self):
  34. if not self.request.user.is_authenticated:
  35. return self.queryset.none()
  36. queryset = self.queryset.filter(
  37. organization__users=self.request.user
  38. ).prefetch_related("team_set")
  39. organization_slug = self.kwargs.get("organization_slug")
  40. if organization_slug:
  41. queryset = queryset.filter(organization__slug=organization_slug)
  42. team_slug = self.kwargs.get("team_slug")
  43. if team_slug:
  44. queryset = queryset.filter(team__slug=team_slug)
  45. return queryset
  46. class ProjectViewSet(
  47. mixins.DestroyModelMixin, mixins.UpdateModelMixin, BaseProjectViewSet
  48. ):
  49. """
  50. /api/0/projects/
  51. Includes organization
  52. Detail view includes teams
  53. """
  54. serializer_class = ProjectSerializer
  55. filter_backends = [OrderingFilter]
  56. ordering = ["name"]
  57. ordering_fields = ["name"]
  58. lookup_field = "pk"
  59. lookup_value_regex = r"(?P<organization_slug>[^/.]+)/(?P<project_slug>[-\w]+)"
  60. def get_serializer_class(self):
  61. if self.action in ["retrieve"]:
  62. return ProjectDetailSerializer
  63. return super().get_serializer_class()
  64. def get_queryset(self):
  65. queryset = super().get_queryset().select_related("organization")
  66. if self.action in ["retrieve"]:
  67. queryset = queryset.prefetch_related("team_set")
  68. return queryset
  69. class TeamProjectViewSet(
  70. mixins.CreateModelMixin,
  71. mixins.UpdateModelMixin,
  72. mixins.DestroyModelMixin,
  73. BaseProjectViewSet,
  74. ):
  75. """
  76. Detail view is under /api/0/projects/{organization_slug}/{project_slug}/
  77. Project keys/DSN's are available at /api/0/projects/{organization_slug}/{project_slug}/keys/
  78. """
  79. serializer_class = ProjectDetailSerializer
  80. def perform_create(self, serializer):
  81. team = None
  82. if self.kwargs.get("team_slug"):
  83. try:
  84. team = Team.objects.get(
  85. slug=self.kwargs.get("team_slug"),
  86. organization__slug=self.kwargs.get("organization_slug"),
  87. organization__users=self.request.user,
  88. organization__organization_users__role__gte=OrganizationUserRole.ADMIN,
  89. )
  90. except Team.DoesNotExist as err:
  91. raise exceptions.ValidationError("Team not found") from err
  92. try:
  93. organization = Organization.objects.get(
  94. slug=self.kwargs.get("organization_slug"),
  95. users=self.request.user,
  96. organization_users__role__gte=OrganizationUserRole.ADMIN,
  97. )
  98. except Organization.DoesNotExist as err:
  99. raise exceptions.ValidationError("Organization not found") from err
  100. new_project = serializer.save(organization=organization)
  101. if new_project and team:
  102. new_project.team_set.add(team)
  103. class OrganizationProjectsViewSet(BaseProjectViewSet):
  104. """
  105. /organizations/<org-slug>/projects/
  106. Includes teams
  107. """
  108. serializer_class = OrganizationProjectSerializer
  109. def get_queryset(self, *args, **kwargs):
  110. queryset = super().get_queryset(*args, **kwargs)
  111. queries = self.request.GET.get("query")
  112. # Pretty simplistic filters that don't match how django-filter works
  113. # If this needs used more extensively, it should be abstracted more
  114. if queries:
  115. for query in queries.split():
  116. query_part = query.split(":", 1)
  117. if len(query_part) == 2:
  118. query_name, query_value = query_part
  119. if query_name == "team":
  120. queryset = queryset.filter(team__slug=query_value)
  121. if query_name == "!team":
  122. queryset = queryset.exclude(team__slug=query_value)
  123. return queryset
  124. class ProjectKeyViewSet(viewsets.ModelViewSet):
  125. queryset = ProjectKey.objects.all()
  126. serializer_class = ProjectKeySerializer
  127. lookup_field = "public_key"
  128. permission_classes = [ProjectKeyPermission]
  129. def get_queryset(self):
  130. if not self.request.user.is_authenticated:
  131. return self.queryset.none()
  132. return (
  133. super()
  134. .get_queryset()
  135. .filter(
  136. project__slug=self.kwargs["project_slug"],
  137. project__organization__slug=self.kwargs["organization_slug"],
  138. project__organization__users=self.request.user,
  139. )
  140. )
  141. def perform_create(self, serializer):
  142. project = get_object_or_404(
  143. Project,
  144. slug=self.kwargs.get("project_slug"),
  145. organization__slug=self.kwargs["organization_slug"],
  146. organization__users=self.request.user,
  147. )
  148. serializer.save(project=project)
  149. class ProjectTeamViewSet(NestedTeamViewSet):
  150. @action(
  151. methods=["post", "delete"], detail=False, url_path=("(?P<team_slug>[-\w]+)")
  152. )
  153. def add_remove_project(
  154. self,
  155. request,
  156. project_pk=None,
  157. project_slug=None,
  158. organization_slug=None,
  159. team_slug=None,
  160. ):
  161. """Add/remove team to a project"""
  162. team = get_object_or_404(self.get_queryset(), slug=team_slug)
  163. project = get_object_or_404(
  164. Project,
  165. slug=project_slug,
  166. organization__slug=organization_slug,
  167. organization__users=self.request.user,
  168. organization__organization_users__role__gte=OrganizationUserRole.MANAGER,
  169. )
  170. serializer = ProjectSerializer(instance=project, context={"request": request})
  171. if request.method == "POST":
  172. project.team_set.add(team)
  173. return Response(serializer.data, status=status.HTTP_201_CREATED)
  174. project.team_set.remove(team)
  175. return Response(serializer.data)