Issue model introduced and used

This commit is contained in:
Klaas van Schelven
2023-11-05 17:43:05 +01:00
parent fc7e186918
commit 972fd99697
13 changed files with 132 additions and 10 deletions

View File

@@ -1,4 +1,5 @@
from django.db import migrations, models
import django.db.models.deletion
import uuid
@@ -7,6 +8,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('projects', '0001_initial'),
]
operations = [
@@ -16,6 +18,7 @@ class Migration(migrations.Migration):
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('data', models.TextField()),
('timestamp', models.DateTimeField(auto_now_add=True, help_text='Server-side timestamp')),
('project', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='projects.project')),
],
),
]

View File

@@ -8,6 +8,9 @@ from rest_framework.views import APIView
# from sentry.utils.auth import parse_auth_header
from projects.models import Project
from issues.models import Issue
from issues.utils import get_hash_for_data
from .negotiation import IgnoreClientContentNegotiation
from .parsers import EnvelopeParser
@@ -21,11 +24,18 @@ class BaseIngestAPIView(APIView):
http_method_names = ["post"]
def process_event(self, event_data, request, project):
DecompressedEvent.objects.create(
event = DecompressedEvent.objects.create(
project=project,
data=json.dumps(event_data), # TODO don't parse-then-print for BaseIngestion
)
hash_ = get_hash_for_data(event_data)
issue, _ = Issue.objects.get_or_create(
hash=hash_,
)
issue.events.add(event)
class IngestEventAPIView(BaseIngestAPIView):
@@ -43,9 +53,11 @@ class IngestEnvelopeAPIView(BaseIngestAPIView):
if len(request.data) != 3:
# multi-part envelopes trigger an error too
print("!= 3")
return Response({"message": "Missing headers / unsupported type"}, status=status.HTTP_501_NOT_IMPLEMENTED)
if request.data[1].get("type") != "event":
print("!= event")
return Response({"message": "Only events are supported"}, status=status.HTTP_501_NOT_IMPLEMENTED)
event = request.data[2]

View File

@@ -1,3 +1,20 @@
from django.contrib import admin
# Register your models here.
from .models import Issue
@admin.register(Issue)
class IssueAdmin(admin.ModelAdmin):
list_display = [
"hash",
"event_count", # expensive operation as written now (query in loop)
]
exclude = ["events"]
readonly_fields = [
'event_count',
]
def event_count(self, obj):
return str(obj.events.count())
event_count.short_description = "Event count"

View File

@@ -0,0 +1,22 @@
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
('ingest', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Issue',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('hash', models.CharField(max_length=32)),
('events', models.ManyToManyField(to='ingest.decompressedevent')),
],
),
]

View File

@@ -6,12 +6,12 @@ class Migration(migrations.Migration):
dependencies = [
('projects', '0001_initial'),
('ingest', '0001_initial'),
('issues', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='decompressedevent',
model_name='issue',
name='project',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='projects.project'),
),

View File

@@ -1,3 +1,14 @@
import uuid
from django.db import models
# Create your models here.
class Issue(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
project = models.ForeignKey(
"projects.Project", blank=False, null=True, on_delete=models.SET_NULL) # SET_NULL: cleanup 'later'
hash = models.CharField(max_length=32, blank=False, null=False)
events = models.ManyToManyField("ingest.DecompressedEvent")
def get_absolute_url(self):
return f"/issues/issue/{ self.id }/events/"

View File

@@ -0,0 +1,12 @@
<html>
<h1>Events for issue {{ issue.id }}</h1>
{% for event in event_list %}
<a href="/events/event/{{ event.id }}/">{{ event.id }} </a><br>
{% endfor %}
</html>

View File

@@ -0,0 +1,12 @@
<html>
<h1>Issues for project {{ project_id }}</h1>
{% for issue in issue_list %}
<a href="/issues/issue/{{ issue.id }}/events/">{{ issue.id }} </a><br>
{% endfor %}
</html>

10
issues/urls.py Normal file
View File

@@ -0,0 +1,10 @@
from django.urls import path
from .views import issue_list, issue_event_list
urlpatterns = [
# path('issue/<uuid:pk>/', issue_detail),
path('<int:project_id>/', issue_list),
path('issue/<uuid:issue_pk>/events/', issue_event_list),
]

View File

@@ -1,3 +1,25 @@
from django.shortcuts import render
# Create your views here.
from .models import Issue
def issue_list(request, project_id):
issue_list = Issue.objects.filter(project_id=project_id)
return render(request, "issues/issue_list.html", {
"project_id": project_id,
"issue_list": issue_list,
})
def issue_event_list(request, issue_pk):
issue = Issue.objects.get(pk=issue_pk)
# note: once we have "Event" (with parsed info) we'll point straight to Issue from there which reduces the nr of
# tables this join goes through by 1.
event_list = issue.events.all()
return render(request, "issues/issue_event_list.html", {
"issue": issue,
"event_list": event_list,
})

View File

@@ -10,6 +10,7 @@ urlpatterns = [
path('api/', include('ingest.urls')),
path('events/', include('events.urls')),
path('issues/', include('issues.urls')),
path('admin/', admin.site.urls),
]

View File

@@ -1,5 +1,4 @@
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
@@ -13,7 +12,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Project',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
),
]

View File

@@ -1,6 +1,7 @@
import uuid
from django.db import models
class Project(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# id is implied which makes it an Integer; we would prefer a uuid but the sentry clients have int baked into the DSN
# parser (we could also introduce a special field for that purpose but that's ugly too)
pass