mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
Canonical API 'skeleton': urls & views
this gives me something to look at and work with, despite being wildly incomplete See #146
This commit is contained in:
@@ -66,8 +66,21 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
'tailwind', # As currently set up, this is also needed in production (templatetags)
|
'tailwind', # As currently set up, this is also needed in production (templatetags)
|
||||||
'admin_auto_filters',
|
'admin_auto_filters',
|
||||||
|
'rest_framework',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # from the tutorial
|
||||||
|
'PAGE_SIZE': 10,
|
||||||
|
|
||||||
|
"DEFAULT_RENDERER_CLASSES": [
|
||||||
|
"rest_framework.renderers.JSONRenderer",
|
||||||
|
],
|
||||||
|
"DEFAULT_PARSER_CLASSES": [
|
||||||
|
"rest_framework.parsers.JSONParser",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
BUGSINK_APPS = [
|
BUGSINK_APPS = [
|
||||||
'bsmain',
|
'bsmain',
|
||||||
'phonehome',
|
'phonehome',
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from django.urls import include, path
|
|||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
from django.views.generic import RedirectView, TemplateView
|
from django.views.generic import RedirectView, TemplateView
|
||||||
|
|
||||||
|
from rest_framework import routers
|
||||||
|
|
||||||
from alerts.views import debug_email as debug_alerts_email
|
from alerts.views import debug_email as debug_alerts_email
|
||||||
from users.views import debug_email as debug_users_email
|
from users.views import debug_email as debug_users_email
|
||||||
from teams.views import debug_email as debug_teams_email
|
from teams.views import debug_email as debug_teams_email
|
||||||
@@ -14,6 +16,12 @@ from ingest.views import download_envelope
|
|||||||
from files.views import chunk_upload, artifact_bundle_assemble, api_root, api_catch_all
|
from files.views import chunk_upload, artifact_bundle_assemble, api_root, api_catch_all
|
||||||
from bugsink.decorators import login_exempt
|
from bugsink.decorators import login_exempt
|
||||||
|
|
||||||
|
from events.api_views import EventViewSet
|
||||||
|
from issues.api_views import IssueViewSet, GroupingViewSet
|
||||||
|
from projects.api_views import ProjectViewSet
|
||||||
|
from releases.api_views import ReleaseViewSet
|
||||||
|
from teams.api_views import TeamViewSet
|
||||||
|
|
||||||
from .views import home, trigger_error, favicon, settings_view, silence_email_system_warning, counts, health_check_ready
|
from .views import home, trigger_error, favicon, settings_view, silence_email_system_warning, counts, health_check_ready
|
||||||
from .debug_views import csrf_debug
|
from .debug_views import csrf_debug
|
||||||
|
|
||||||
@@ -23,6 +31,15 @@ admin.site.site_title = get_settings().SITE_TITLE
|
|||||||
admin.site.index_title = "Admin" # everyone calls this the "admin" anyway. Let's set the title accordingly.
|
admin.site.index_title = "Admin" # everyone calls this the "admin" anyway. Let's set the title accordingly.
|
||||||
|
|
||||||
|
|
||||||
|
api_router = routers.DefaultRouter()
|
||||||
|
api_router.register(r'events', EventViewSet)
|
||||||
|
api_router.register(r'issues', IssueViewSet)
|
||||||
|
api_router.register(r'groupings', GroupingViewSet)
|
||||||
|
api_router.register(r'projects', ProjectViewSet)
|
||||||
|
api_router.register(r'releases', ReleaseViewSet)
|
||||||
|
api_router.register(r'teams', TeamViewSet)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', home, name='home'),
|
path('', home, name='home'),
|
||||||
|
|
||||||
@@ -43,6 +60,8 @@ urlpatterns = [
|
|||||||
# many user-related views are directly exposed above (/accounts/), the rest is here:
|
# many user-related views are directly exposed above (/accounts/), the rest is here:
|
||||||
path("users/", include("users.urls")),
|
path("users/", include("users.urls")),
|
||||||
|
|
||||||
|
path("api/canonical/0/", include(api_router.urls)),
|
||||||
|
|
||||||
# these are sentry-cli endpoint for uploading; they're unrelated to e.g. the ingestion API.
|
# these are sentry-cli endpoint for uploading; they're unrelated to e.g. the ingestion API.
|
||||||
# the /api/0/ is just a hard prefix (for the ingest API, that position indicates the project id, but here it's just
|
# the /api/0/ is just a hard prefix (for the ingest API, that position indicates the project id, but here it's just
|
||||||
# a prefix)
|
# a prefix)
|
||||||
|
|||||||
11
events/api_views.py
Normal file
11
events/api_views.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Event
|
||||||
|
from .serializers import EventSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class EventViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
queryset = Event.objects.all().order_by('digest_order')
|
||||||
|
serializer_class = EventSerializer
|
||||||
|
|
||||||
|
# TODO: the idea of required filter-fields when listing.
|
||||||
25
events/serializers.py
Normal file
25
events/serializers.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Event
|
||||||
|
|
||||||
|
|
||||||
|
class EventSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Event
|
||||||
|
|
||||||
|
# TODO better wording:
|
||||||
|
# This is the first attempt at getting the list of fields right. My belief is: this is a nice minimal list.
|
||||||
|
# it _does_ contain `data`, which is typically quite "fat", but I'd say that's the most useful field to have.
|
||||||
|
# and when you're actually in the business of looking at a specific event, you want to see the data.
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"ingested_at",
|
||||||
|
"digested_at",
|
||||||
|
"issue",
|
||||||
|
"grouping",
|
||||||
|
"event_id",
|
||||||
|
"project",
|
||||||
|
"data", # TODO fetch from disk if so-configured
|
||||||
|
"timestamp",
|
||||||
|
"digest_order",
|
||||||
|
]
|
||||||
16
issues/api_views.py
Normal file
16
issues/api_views.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Issue, Grouping
|
||||||
|
from .serializers import IssueSerializer, GroupingSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class IssueViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
queryset = Issue.objects.all().order_by('digest_order') # TBD
|
||||||
|
serializer_class = IssueSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class GroupingViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
queryset = Grouping.objects.all().order_by('grouping_key') # TBD
|
||||||
|
serializer_class = GroupingSerializer
|
||||||
|
|
||||||
|
# TODO: the idea of required filter-fields when listing.
|
||||||
45
issues/serializers.py
Normal file
45
issues/serializers.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Issue, Grouping
|
||||||
|
|
||||||
|
|
||||||
|
class IssueSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Issue
|
||||||
|
|
||||||
|
# TODO better wording:
|
||||||
|
# This is the first attempt at getting the list of fields right. My belief is: this is a nice minimal list.
|
||||||
|
# it _does_ contain `data`, which is typically quite "fat", but I'd say that's the most useful field to have.
|
||||||
|
# and when you're actually in the business of looking at a specific event, you want to see the data.
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"project",
|
||||||
|
"is_deleted",
|
||||||
|
"digest_order",
|
||||||
|
"last_seen",
|
||||||
|
"first_seen",
|
||||||
|
"digested_event_count",
|
||||||
|
"stored_event_count",
|
||||||
|
"calculated_type",
|
||||||
|
"calculated_value",
|
||||||
|
"transaction",
|
||||||
|
# "last_frame_filename",
|
||||||
|
# "last_frame_module",
|
||||||
|
# "last_frame_function",
|
||||||
|
"is_resolved",
|
||||||
|
"is_resolved_by_next_release",
|
||||||
|
# "fixed_at", too "raw"? i.e. too implementation-tied?
|
||||||
|
# "events_at", too "raw"? i.e. too implementation-tied?
|
||||||
|
"is_muted",
|
||||||
|
# "unmute_on_volume_based_conditions", too "raw"? i.e. too implementation-tied?
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class GroupingSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Grouping
|
||||||
|
fields = [
|
||||||
|
"project",
|
||||||
|
"grouping_key",
|
||||||
|
"issue",
|
||||||
|
]
|
||||||
9
projects/api_views.py
Normal file
9
projects/api_views.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Project
|
||||||
|
from .serializers import ProjectSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Project.objects.all().order_by('id')
|
||||||
|
serializer_class = ProjectSerializer
|
||||||
24
projects/serializers.py
Normal file
24
projects/serializers.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Project
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Project
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"team",
|
||||||
|
"name",
|
||||||
|
"slug",
|
||||||
|
"is_deleted",
|
||||||
|
"sentry_key", # or just: "dsn"
|
||||||
|
# users = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, through="ProjectMembership")
|
||||||
|
"digested_event_count",
|
||||||
|
"stored_event_count",
|
||||||
|
"alert_on_new_issue",
|
||||||
|
"alert_on_regression",
|
||||||
|
"alert_on_unmute",
|
||||||
|
"visibility",
|
||||||
|
"retention_max_event_count",
|
||||||
|
]
|
||||||
11
releases/api_views.py
Normal file
11
releases/api_views.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Release
|
||||||
|
from .serializers import ReleaseSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Release.objects.all().order_by('sort_epoch')
|
||||||
|
serializer_class = ReleaseSerializer
|
||||||
|
|
||||||
|
# TODO: the idea of required filter-fields when listing; in particular: project is required.
|
||||||
19
releases/serializers.py
Normal file
19
releases/serializers.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Release
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Release
|
||||||
|
|
||||||
|
# TODO: distinguish read vs write fields
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"project",
|
||||||
|
"version",
|
||||||
|
"date_released",
|
||||||
|
"semver",
|
||||||
|
"is_semver",
|
||||||
|
"sort_epoch",
|
||||||
|
]
|
||||||
@@ -16,3 +16,4 @@ user-agents==2.2.*
|
|||||||
fastjsonschema==2.21.*
|
fastjsonschema==2.21.*
|
||||||
verbose_csrf_middleware==1.0.*
|
verbose_csrf_middleware==1.0.*
|
||||||
ecma426>=0.2.0
|
ecma426>=0.2.0
|
||||||
|
djangorestframework==3.16.*
|
||||||
|
|||||||
9
teams/api_views.py
Normal file
9
teams/api_views.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from .models import Team
|
||||||
|
from .serializers import TeamSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class TeamViewSet(viewsets.ReadOnlyModelViewSet): # create? then we need a way to deal with visibility
|
||||||
|
queryset = Team.objects.all().order_by('name') # ordering: TBD
|
||||||
|
serializer_class = TeamSerializer
|
||||||
13
teams/serializers.py
Normal file
13
teams/serializers.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import Team
|
||||||
|
|
||||||
|
|
||||||
|
class TeamSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Team
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"name",
|
||||||
|
# "visibility",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user