mirror of
https://github.com/bugsink/bugsink.git
synced 2026-01-06 21:30:18 -06:00
92 lines
3.7 KiB
Python
92 lines
3.7 KiB
Python
import uuid
|
|
|
|
from django.db import models
|
|
from django.conf import settings
|
|
from django.utils.text import slugify
|
|
|
|
from bugsink.app_settings import get_settings
|
|
|
|
from compat.dsn import build_dsn
|
|
|
|
|
|
class Project(models.Model):
|
|
# 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)
|
|
|
|
name = models.CharField(max_length=255, blank=False, null=False)
|
|
slug = models.SlugField(max_length=50, blank=False, null=False)
|
|
|
|
# sentry_key mirrors the "public" part of the sentry DSN. As of late 2023 Sentry's docs say the this about DSNs:
|
|
#
|
|
# > DSNs are safe to keep public because they only allow submission of new events and related event data; they do
|
|
# > not allow read access to any information.
|
|
#
|
|
# The "because" in that sentence is dubious at least; however, I get why they say it, because they want to do JS and
|
|
# native apps too, and there's really no way to do those without exposing (some) endpoint. Anyway, I don't think the
|
|
# "public" key is public, and if you can help it it's always better to keep it private.
|
|
sentry_key = models.UUIDField(editable=False, default=uuid.uuid4)
|
|
|
|
users = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, through="ProjectMembership")
|
|
|
|
# We don't implement private_key because as of late 2023 the Sentry documentation says the following:
|
|
# > The secret part of the DSN is optional and effectively deprecated. While clients will still honor it, if
|
|
# > supplied, future versions of Sentry will entirely ignore it.
|
|
# private_key = ...
|
|
|
|
# denormalized/cached fields below
|
|
has_releases = models.BooleanField(editable=False, default=False)
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def get_absolute_url(self):
|
|
return f"/issues/{ self.id }/"
|
|
|
|
@property
|
|
def dsn(self):
|
|
return build_dsn(get_settings().BASE_URL, self.id, self.sentry_key)
|
|
|
|
"""
|
|
# TODO is this even more efficient?
|
|
indexes = [
|
|
models.Index(fields=["id", "sentry_key"]),
|
|
]
|
|
"""
|
|
|
|
# alerting conditions
|
|
alert_on_new_issue = models.BooleanField(default=True)
|
|
alert_on_regression = models.BooleanField(default=True)
|
|
alert_on_unmute = models.BooleanField(default=True)
|
|
|
|
def get_latest_release(self):
|
|
# TODO perfomance considerations... this can be denormalized/cached at the project level
|
|
from releases.models import ordered_releases
|
|
return list(ordered_releases(project=self))[-1]
|
|
|
|
def save(self, *args, **kwargs):
|
|
if self.slug is None:
|
|
# this is not guaranteeing uniqueness but it's enough to have something that makes our tests work.
|
|
# in realy usage slugs are provided properly on-creation.
|
|
self.slug = slugify(self.name)
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class ProjectMembership(models.Model):
|
|
project = models.ForeignKey(Project, on_delete=models.CASCADE)
|
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
|
|
|
# TODO inheriting True/False for None from either Organization (which we also don't have yet) or directly from
|
|
# User(Profile) is something we'll do later. At that point we'll probably implement it as denormalized here, so
|
|
# we'll just have to shift the currently existing field into send_email_alerts_denormalized and create a 3-way
|
|
# field.
|
|
send_email_alerts = models.BooleanField(default=True)
|
|
|
|
# TODO this will come
|
|
# role = models.CharField(max_length=255, blank=False, null=False)
|
|
|
|
def __str__(self):
|
|
return f"{self.user} membership of {self.project}"
|
|
|
|
class Meta:
|
|
unique_together = ("project", "user")
|