SDK setup (WIP)

This commit is contained in:
Klaas van Schelven
2024-06-07 17:24:25 +02:00
parent 784f0584f0
commit b04feae788
8 changed files with 152 additions and 4 deletions

View File

@@ -145,6 +145,17 @@
</div>
</td>
</tr>
{% empty %}
<tr class="bg-slate-50 border-slate-300 border-2 ">
<td>
</td>
<td class="w-full ml-0 pb-4 pt-4 pr-4 text-center">
<div class="p-4">
No issues found.
This might mean you have not yet <a class="text-cyan-500 font-bold" href="{% url "project_sdk_setup" project_pk=project.id %}">set up your SDK</a>.
</div>
</tr>
{% endfor %}
</tbody>

View File

@@ -1,6 +1,7 @@
from django import forms
from django.contrib.auth import get_user_model
from django.template.defaultfilters import yesno
from django.urls import reverse
from teams.models import TeamMembership
@@ -58,19 +59,25 @@ class MyProjectMembershipForm(forms.ModelForm):
class ProjectForm(forms.ModelForm):
dsn = forms.CharField(label="DSN", disabled=True)
def __init__(self, *args, **kwargs):
team_qs = kwargs.pop("team_qs", None)
super().__init__(*args, **kwargs)
if self.instance is not None and self.instance.pk is not None:
if self.instance is None or self.instance.pk is None:
# for editing, we disallow changing the team. consideration: it's somewhat hard to see what the consequences
# for authorization are (from the user's perspective).
del self.fields["team"]
del self.fields["dsn"]
# if we ever push slug to the form, editing it should probably be disallowed as well (but mainly because it
# has consequences on the issue's short identifier)
# del self.fields["slug"]
else:
self.fields["team"].queryset = team_qs
self.fields["dsn"].initial = self.instance.dsn
self.fields["dsn"].label = "DSN (read-only)"
self.fields["dsn"].help_text = 'Use the DSN to <a href="' + reverse('project_sdk_setup', kwargs={'project_pk': self.instance.pk}) + '" class="text-cyan-800 font-bold">set up the SDK</a>.'
class Meta:
model = Project

View File

@@ -4,6 +4,7 @@
{% block title %}Edit {{ project.name }} · {{ site_title }}{% endblock %}
{% block content %}
{# div class="text-cyan-800" here in an attempt to trigger tailwind, which does not pick up Pyhton code #}
<div class="flex items-center justify-center">
@@ -50,6 +51,23 @@
</div>
{% endif %}
{% if form.dsn %}
<div class="text-lg mb-8">
<div class="text-slate-800 font-bold">{{ form.dsn.label }}</div>
<div class="flex items-center">
{{ form.dsn }}
</div>
{% if form.dsn.errors %}
{% for error in form.dsn.errors %}
<div class="text-red-500 pt-1 px-2 text-sm">{{ error }}</div>
{% endfor %}
{% elif form.dsn.help_text %}
<div class="text-gray-500 pt-1 px-2 text-sm">{{ form.dsn.help_text|safe }}</div>
{% endif %}
</div>
{% endif %}
<button name="action" value="invite" class="font-bold text-slate-800 border-slate-500 pl-4 pr-4 pb-2 pt-2 border-2 bg-cyan-200 hover:bg-cyan-400 active:ring rounded-md">Save</button>
<a href="{% url "project_list" %}" class="text-cyan-500 font-bold ml-2">Cancel</a>
</form>

View File

@@ -93,6 +93,16 @@
</div>
</td>
<td class="pr-2">
<div class="rounded-full hover:bg-slate-100 p-2 cursor-pointer" onclick="followContainedLink(this);" >
<a href="{% url 'project_sdk_setup' project_pk=project.id %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-8">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 9.75 16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0 0 20.25 18V6A2.25 2.25 0 0 0 18 3.75H6A2.25 2.25 0 0 0 3.75 6v12A2.25 2.25 0 0 0 6 20.25Z" />
</svg>
</a>
</div>
</td>
<td>
{% if project.member %}
{% if not project.member.accepted %}

View File

@@ -0,0 +1,87 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Set up your SDK · {{ site_title }}{% endblock %}
{% block content %}
<div class="flex items-center justify-center">
<div class="m-4 max-w-4xl flex-auto">
<h1 class="text-4xl font-bold">Set up your SDK</h1>
<div class="mt-4">
To get started, you need to set up your SDK. Follow the instructions below to set up your SDK.
</div>
<div class="mt-4 italic">
Note: currently, we only support the Python SDK. Other SDKs might actually work, but have not been tested.
</div>
<div class="mt-4">
Bugsink is compatible with the Sentry SDK. A basic setup is the following:
</div>
<h2 class="mt-8 text-2xl font-bold">Step 1: Install the SDK</h2>
<div class="mt-4">
Install the SDK using pip:
</div>
<div class="mt-4">
<div class="p-4 mt-4 bg-slate-50 syntax-coloring"><pre><span>pip install sentry-sdk</span></pre></div>
</div>
<h2 class="mt-8 text-2xl font-bold">Step 2: Initialize the SDK</h2>
<div class="mt-4">
Initialize the SDK with your DSN:
</div>
{% comment %} add hoc construction of highlighted code:
from pygments import highlight
from pygments.lexers import PythonLexer
from pygments.formatters import HtmlFormatter
lexer = PythonLexer()
code = """import sentry_sdk
sentry_sdk.init(
"foo",
traces_sample_rate=0, # Bugsink intentionally does not support traces
)"""
print(highlight(code, lexer, HtmlFormatter()))
{% endcomment %}
<div class="p-4 mt-4 bg-slate-50 syntax-coloring"><pre><span></span><span class="kn">import</span> <span class="nn">sentry_sdk</span>
<span class="n">sentry_sdk</span><span class="o">.</span><span class="n">init</span><span class="p">(</span>
<span class="s2">&quot;{{ project.dsn }}&quot;</span><span class="p">,</span>
<span class="n">traces_sample_rate</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="c1"># Bugsink intentionally does not support traces</span>
<span class="p">)</span>
</pre></div>
<div class="mt-4">
For integration-specific (Django, Flask, etc) notes, see the <a href="https://docs.sentry.io/platforms/python/" class="text-cyan-500 font-bold" target="_blank">Sentry documentation</a>.
</div>
<h2 class="mt-6 text-2xl font-bold">Step 3: Verify the setup</h2>
<div class="mt-4">
Verify the setup by sending an event:
</div>
<div class="p-4 mt-4 bg-slate-50 syntax-coloring"><pre><span></span><span class="kn">import</span> <span class="nn">sentry_sdk</span>
<span class="n">division_by_zero</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">/</span> <span class="mi">0</span>
</pre></div>
<div class="mt-4">
Your event should now appear in the <a href="{% url "issue_list_open" project_pk=project.pk %}" class="text-cyan-500 font-bold">list of open issues</a>.
</div>
</div>
</div>
{% endblock %}

View File

@@ -2,10 +2,11 @@ from django.urls import path
from .views import (
project_list, project_members, project_members_accept, project_member_settings, project_members_invite,
project_members_accept_new_user, project_new, project_edit)
project_members_accept_new_user, project_new, project_edit, project_sdk_setup)
urlpatterns = [
path('', project_list, name="project_list"),
path('mine/', project_list, kwargs={"ownership_filter": "mine"}, name="project_list_mine"),
path('teams/', project_list, kwargs={"ownership_filter": "teams"}, name="project_list_teams"),
path('other/', project_list, kwargs={"ownership_filter": "other"}, name="project_list_other"),
@@ -17,4 +18,6 @@ urlpatterns = [
path('<str:project_pk>/members/accept/<str:token>/', project_members_accept_new_user,
name="project_members_accept_new_user"),
path('<int:project_pk>/members/settings/<str:user_pk>/', project_member_settings, name="project_member_settings"),
path('<int:project_pk>/sdk-setup/', project_sdk_setup, name="project_sdk_setup"),
]

View File

@@ -124,7 +124,7 @@ def project_new(request):
# the user who creates the project is automatically an (accepted) admin of the project
ProjectMembership.objects.create(project=project, user=request.user, role=ProjectRole.ADMIN, accepted=True)
return redirect('project_members', project_pk=project.id)
return redirect('project_sdk_setup', project_pk=project.id)
else:
form = ProjectForm(team_qs=team_qs)
@@ -363,3 +363,15 @@ def project_members_accept(request, project_pk):
raise Http404("Invalid action")
return render(request, "projects/project_members_accept.html", {"project": project, "membership": membership})
def project_sdk_setup(request, project_pk):
project = Project.objects.get(id=project_pk)
if not request.user.is_superuser and not ProjectMembership.objects.filter(project=project, user=request.user,
accepted=True).exists():
raise PermissionDenied("You are not a member of this project")
return render(request, "projects/project_sdk_setup.html", {
"project": project,
})

File diff suppressed because one or more lines are too long