mirror of
https://github.com/bugsink/bugsink.git
synced 2025-12-20 03:59:53 -06: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)
|
||||
'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 = [
|
||||
'bsmain',
|
||||
'phonehome',
|
||||
|
||||
@@ -5,6 +5,8 @@ from django.urls import include, path
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.views.generic import RedirectView, TemplateView
|
||||
|
||||
from rest_framework import routers
|
||||
|
||||
from alerts.views import debug_email as debug_alerts_email
|
||||
from users.views import debug_email as debug_users_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 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 .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.
|
||||
|
||||
|
||||
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 = [
|
||||
path('', home, name='home'),
|
||||
|
||||
@@ -43,6 +60,8 @@ urlpatterns = [
|
||||
# many user-related views are directly exposed above (/accounts/), the rest is here:
|
||||
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.
|
||||
# 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)
|
||||
|
||||
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.*
|
||||
verbose_csrf_middleware==1.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