mirror of
https://github.com/bugsink/bugsink.git
synced 2026-02-21 22:09:21 -06:00
Issue model introduced and used
This commit is contained in:
@@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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"
|
||||
|
||||
22
issues/migrations/0001_initial.py
Normal file
22
issues/migrations/0001_initial.py
Normal 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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -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'),
|
||||
),
|
||||
@@ -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/"
|
||||
|
||||
12
issues/templates/issues/issue_event_list.html
Normal file
12
issues/templates/issues/issue_event_list.html
Normal 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>
|
||||
12
issues/templates/issues/issue_list.html
Normal file
12
issues/templates/issues/issue_list.html
Normal 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
10
issues/urls.py
Normal 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),
|
||||
]
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -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')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user