David Burke 3 лет назад
Родитель
Сommit
7b634b4eb9
5 измененных файлов с 123 добавлено и 15 удалено
  1. 11 1
      organizations_ext/urls.py
  2. 42 0
      performance/admin.py
  3. 9 0
      performance/models.py
  4. 11 12
      performance/serializers.py
  5. 50 2
      performance/views.py

+ 11 - 1
organizations_ext/urls.py

@@ -4,7 +4,7 @@ from issues.views import IssueViewSet
 from teams.views import NestedTeamViewSet
 from environments.views import EnvironmentViewSet
 from releases.views import ReleaseViewSet
-from performance.views import TransactionViewSet
+from performance.views import TransactionViewSet, TransactionGroupViewSet, SpanViewSet
 from glitchtip.uptime.views import MonitorViewSet, MonitorCheckViewSet
 from glitchtip.routers import BulkSimpleRouter
 from .views import (
@@ -43,6 +43,16 @@ organizations_router.register(
 organizations_router.register(
     r"transactions", TransactionViewSet, basename="organization-transactions"
 )
+organizations_router.register(
+    r"transaction-groups",
+    TransactionGroupViewSet,
+    basename="organization-transaction-groups",
+)
+organizations_router.register(
+    r"spans",
+    SpanViewSet,
+    basename="organization-spans",
+)
 organizations_router.register(
     r"monitors", MonitorViewSet, basename="organization-monitors"
 )

+ 42 - 0
performance/admin.py

@@ -0,0 +1,42 @@
+from django.contrib import admin
+from .models import TransactionGroup, TransactionEvent, Span
+
+
+class TransactionGroupAdmin(admin.ModelAdmin):
+    search_fields = ["title", "op"]
+    list_filter = ["created", "op", "method"]
+
+
+class SpanInline(admin.TabularInline):
+    model = Span
+    extra = 0
+    readonly_fields = [
+        "span_id",
+        "parent_span_id",
+        "op",
+        "description",
+        "start_timestamp",
+        "timestamp",
+        "tags",
+        "data",
+    ]
+
+    def has_add_permission(self, request, *args, **kwargs):
+        return False
+
+
+class TransactionEventAdmin(admin.ModelAdmin):
+    search_fields = ["trace_id", "transaction"]
+    list_filter = ["created"]
+    inlines = [SpanInline]
+    can_delete = False
+
+
+class SpanAdmin(admin.ModelAdmin):
+    search_fields = ["span_id", "op", "description", "transaction__trace_id"]
+    list_filter = ["created"]
+
+
+admin.site.register(TransactionGroup, TransactionGroupAdmin)
+admin.site.register(TransactionEvent, TransactionEventAdmin)
+admin.site.register(Span, SpanAdmin)

+ 9 - 0
performance/models.py

@@ -13,6 +13,9 @@ class TransactionGroup(CreatedModel):
     class Meta:
         unique_together = (("title", "project", "op", "method"),)
 
+    def __str__(self):
+        return self.title
+
 
 class TransactionEvent(AbstractEvent):
     group = models.ForeignKey(TransactionGroup, on_delete=models.CASCADE)
@@ -23,6 +26,9 @@ class TransactionEvent(AbstractEvent):
     class Meta:
         ordering = ["-created"]
 
+    def __str__(self):
+        return str(self.trace_id)
+
 
 class Span(CreatedModel):
     transaction = models.ForeignKey(TransactionEvent, on_delete=models.CASCADE)
@@ -35,3 +41,6 @@ class Span(CreatedModel):
     timestamp = models.DateTimeField()
     tags = HStoreField(default=dict)
     data = HStoreField(default=dict)
+
+    def __str__(self):
+        return self.span_id

+ 11 - 12
performance/serializers.py

@@ -4,6 +4,17 @@ from glitchtip.serializers import FlexibleDateTimeField
 from .models import TransactionEvent, TransactionGroup, Span
 
 
+class TransactionGroupSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = TransactionGroup
+        fields = [
+            "title",
+            "project",
+            "op",
+            "method",
+        ]
+
+
 class SpanSerializer(serializers.ModelSerializer):
     class Meta:
         model = Span
@@ -18,18 +29,6 @@ class SpanSerializer(serializers.ModelSerializer):
             "data",
         ]
 
-    # data = serializers.JSONField(required=False)
-    # description = serializers.CharField(required=False)
-    # op = serializers.CharField(required=False)
-    # parent_span_id = serializers.CharField(required=False)
-    # span_id = serializers.CharField(required=False)
-    # start_timestamp = FlexibleDateTimeField()
-    # status = serializers.CharField(required=False)
-    # tags = serializers.JSONField(required=False)
-    # timestamp = FlexibleDateTimeField()
-    # trace_id = serializers.UUIDField()
-    # same_process_as_parent = serializers.BooleanField(required=False)
-
 
 class TransactionEventSerializer(SentrySDKEventSerializer):
     type = serializers.CharField()

+ 50 - 2
performance/views.py

@@ -1,7 +1,32 @@
 from rest_framework import viewsets
 from projects.models import Project
-from .models import TransactionEvent
-from .serializers import TransactionSerializer
+from .models import TransactionEvent, TransactionGroup, Span
+from .serializers import (
+    TransactionSerializer,
+    TransactionGroupSerializer,
+    SpanSerializer,
+)
+
+
+class TransactionGroupViewSet(viewsets.ReadOnlyModelViewSet):
+    queryset = TransactionGroup.objects.all()
+    serializer_class = TransactionGroupSerializer
+
+    def get_queryset(self):
+        if not self.request.user.is_authenticated:
+            return self.queryset.none()
+        # Performance optimization, force two queries
+        projects = list(
+            Project.objects.filter(team__members__user=self.request.user).values_list(
+                "pk", flat=True
+            )
+        )
+        qs = super().get_queryset().filter(project__pk__in=projects)
+        if "organization_slug" in self.kwargs:
+            qs = qs.filter(
+                project__organization__slug=self.kwargs["organization_slug"],
+            )
+        return qs
 
 
 class TransactionViewSet(viewsets.ReadOnlyModelViewSet):
@@ -23,3 +48,26 @@ class TransactionViewSet(viewsets.ReadOnlyModelViewSet):
                 group__project__organization__slug=self.kwargs["organization_slug"],
             )
         return qs
+
+
+class SpanViewSet(viewsets.ReadOnlyModelViewSet):
+    queryset = Span.objects.all()
+    serializer_class = SpanSerializer
+
+    def get_queryset(self):
+        if not self.request.user.is_authenticated:
+            return self.queryset.none()
+        # Performance optimization, force two queries
+        projects = list(
+            Project.objects.filter(team__members__user=self.request.user).values_list(
+                "pk", flat=True
+            )
+        )
+        qs = super().get_queryset().filter(transaction__group__project__pk__in=projects)
+        if "organization_slug" in self.kwargs:
+            qs = qs.filter(
+                transaction__group__project__organization__slug=self.kwargs[
+                    "organization_slug"
+                ],
+            )
+        return qs