class GroupTagValue(Model): """ Stores the total number of messages seen by a group matching the given filter. """ project = models.ForeignKey('sentry.Project', null=True, related_name='grouptag') group = models.ForeignKey('sentry.Group', related_name='grouptag') times_seen = BoundedPositiveIntegerField(default=0) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) last_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_messagefiltervalue' unique_together = (('project', 'key', 'value', 'group'),) __repr__ = sane_repr('project_id', 'group_id', 'key', 'value') def save(self, *args, **kwargs): if not self.first_seen: self.first_seen = self.last_seen super(GroupTag, self).save(*args, **kwargs)
def create(self, id, secret=None, homepage=None, capabilities_url=None, room_id=None, token_url=None, capdoc=None): if homepage is None and capdoc is not None: homepage = capdoc['links']['homepage'] if token_url is None and capdoc is not None: token_url = capdoc['capabilities']['oauth2Provider']['tokenUrl'] if capabilities_url is None and capdoc is not None: capabilities_url = capdoc['links']['self'] if capdoc is not None: api_base_url = capdoc['capabilities']['hipchatApiProvider']['url'] else: api_base_url = capabilities_url.rsplit('/', 1)[0] installed_from = token_url and base_url(token_url) or None return BaseManager.create(self, id=id, room_id=room_id, secret=secret, homepage=homepage, token_url=token_url, capabilities_url=capabilities_url, api_base_url=api_base_url, installed_from=installed_from, )
def create_or_update_app(self, sync_id, app_id, project, data=None, platform=DSymPlatform.GENERIC, no_fetch=False): if data is None: data = {} if not no_fetch: _auto_enrich_data(data, app_id, platform) existing_app = DSymApp.objects.filter(app_id=app_id, project=project).first() if existing_app is not None: now = timezone.now() existing_app.update( sync_id=sync_id, data=data, last_synced=now, ) return existing_app return BaseManager.create(self, sync_id=sync_id, app_id=app_id, data=data, project=project, platform=platform)
class TagValue(Model): """ Stores references to available filters. """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True, null=True) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_filtervalue' unique_together = (('project_id', 'key', 'value'), ) index_together = (('project_id', 'key', 'last_seen'), ) __repr__ = sane_repr('project_id', 'key', 'value') def get_label(self): from sentry import tagstore return tagstore.get_tag_value_label(self.key, self.value)
def create(self, id, secret=None, homepage=None, capabilities_url=None, room_id=None, token_url=None, capdoc=None): if homepage is None and capdoc is not None: homepage = capdoc['links']['homepage'] if token_url is None and capdoc is not None: token_url = capdoc['capabilities']['oauth2Provider']['tokenUrl'] if capabilities_url is None and capdoc is not None: capabilities_url = capdoc['links']['self'] if capdoc is not None: api_base_url = capdoc['capabilities']['hipchatApiProvider']['url'] else: api_base_url = capabilities_url.rsplit('/', 1)[0] installed_from = token_url and base_url(token_url) or None return BaseManager.create( self, id=id, room_id=room_id, secret=secret, homepage=homepage, token_url=token_url, capabilities_url=capabilities_url, api_base_url=api_base_url, installed_from=installed_from, )
class GroupEmailThread(Model): """ Keep track of the original Message-Id that was sent unique per email destination and Group object.This allows the tracking of proper In-Reply-To and References headers for email threading. """ __include_in_export__ = False email = models.EmailField(max_length=75) project = FlexibleForeignKey("sentry.Project", related_name="groupemail_set") group = FlexibleForeignKey("sentry.Group", related_name="groupemail_set") msgid = models.CharField(max_length=100) date = models.DateTimeField(default=timezone.now, db_index=True) objects = BaseManager() class Meta: app_label = "sentry" db_table = "sentry_groupemailthread" unique_together = (("email", "group"), ("email", "msgid")) indexes = [models.Index(fields=["date", "project", "id"])] __repr__ = sane_repr("email", "group_id", "msgid")
class ServiceHook(Model): __include_in_export__ = True guid = models.CharField(max_length=32, unique=True, null=True) # hooks may be bound to an api application, or simply registered by a user application = FlexibleForeignKey("sentry.ApiApplication", null=True) actor_id = BoundedPositiveIntegerField(db_index=True) project_id = BoundedPositiveIntegerField(db_index=True, null=True) organization_id = BoundedPositiveIntegerField(db_index=True, null=True) url = models.URLField(max_length=512) secret = EncryptedTextField(default=generate_secret) events = ArrayField(of=models.TextField) status = BoundedPositiveIntegerField(default=0, choices=ObjectStatus.as_choices(), db_index=True) version = BoundedPositiveIntegerField(default=0, choices=((0, "0"), )) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=("guid", )) class Meta: app_label = "sentry" db_table = "sentry_servicehook" __repr__ = sane_repr("guid", "project_id") @property def created_by_sentry_app(self): return self.application_id and self.sentry_app @property def sentry_app(self): try: return SentryApp.objects.get(application_id=self.application_id) except SentryApp.DoesNotExist: return def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.guid is None: self.guid = uuid4().hex def __str__(self): return str(self.guid) def build_signature(self, body): return hmac.new(key=self.secret.encode("utf-8"), msg=body.encode("utf-8"), digestmod=sha256).hexdigest() def get_audit_log_data(self): return {"url": self.url} def add_project(self, project): """ Add a project to the service hook. """ ServiceHookProject.objects.create(project_id=project.id, service_hook_id=self.id)
class AccessGroup(Model): """ An access group identifies a set of members with a defined set of permissions (and project access) for a Team. Groups may be automated through extensions (such as LDAP) so that membership is automatically maintained. If this is the case the ``managed`` attribute will be ``True``. """ team = models.ForeignKey('sentry.Team') name = models.CharField(max_length=64) type = BoundedIntegerField(choices=MEMBER_TYPES, default=MEMBER_USER) managed = models.BooleanField(default=False) data = GzippedDictField(blank=True, null=True) date_added = models.DateTimeField(default=timezone.now) projects = models.ManyToManyField('sentry.Project') members = models.ManyToManyField(settings.AUTH_USER_MODEL) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_accessgroup' unique_together = (('team', 'name'),) __repr__ = sane_repr('team_id', 'name', 'type', 'managed')
class MonitorCheckIn(Model): __core__ = False guid = UUIDField(unique=True, auto_add=True) project_id = BoundedPositiveIntegerField(db_index=True) monitor = FlexibleForeignKey("sentry.Monitor") location = FlexibleForeignKey("sentry.MonitorLocation", null=True) status = BoundedPositiveIntegerField(default=0, choices=CheckInStatus.as_choices()) config = EncryptedJsonField(default=dict) duration = BoundedPositiveIntegerField(null=True) date_added = models.DateTimeField(default=timezone.now) date_updated = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=("guid", )) class Meta: app_label = "sentry" db_table = "sentry_monitorcheckin" __repr__ = sane_repr("guid", "project_id", "status") def save(self, *args, **kwargs): if not self.date_added: self.date_added = timezone.now() if not self.date_updated: self.date_updated = self.date_added return super(MonitorCheckIn, self).save(*args, **kwargs) # XXX(dcramer): BaseModel is trying to automatically set date_updated which is not # what we want to happen, so kill it here def _update_timestamps(self): pass
class TagValue(Model): """ Stores references to available filters. """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True, null=True) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_filtervalue' unique_together = (('project_id', 'key', 'value'), ) __repr__ = sane_repr('project_id', 'key', 'value') def get_label(self): # HACK(dcramer): quick and dirty way to hack in better display states if self.key == 'sentry:release': return Release.get_display_version(self.value) return self.value
class GroupTagKey(Model): """ Stores a unique tag key name for a group. An example key might be "url" or "server_name". """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True, null=True) group_id = BoundedPositiveIntegerField(db_index=True) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) values_seen = BoundedPositiveIntegerField(default=0) objects = BaseManager() class Meta: app_label = "sentry" db_table = "sentry_grouptagkey" unique_together = (("project_id", "group_id", "key"), ) __repr__ = sane_repr("project_id", "group_id", "key") def merge_counts(self, new_group): from sentry.tagstore.legacy.models import GroupTagValue try: with transaction.atomic(using=router.db_for_write(GroupTagKey)): GroupTagKey.objects.filter( group_id=new_group.id, key=self.key).update( values_seen=GroupTagValue.objects.filter( group_id=new_group.id, key=self.key).count()) except DataError: # it's possible to hit an out of range value for counters pass
class TeamMember(Model): """ Identifies relationships between teams and users. Users listed as team members are considered to have access to all projects and could be thought of as team owners (though their access level may not) be set to ownership. """ team = FlexibleForeignKey('sentry.Team', related_name=None) user = FlexibleForeignKey(settings.AUTH_USER_MODEL, related_name=None) type = BoundedIntegerField(choices=( (TeamMemberType.MEMBER, _('Member')), (TeamMemberType.ADMIN, _('Admin')), (TeamMemberType.BOT, _('Bot')), ), default=TeamMemberType.MEMBER) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_teammember' unique_together = (('team', 'user'), ) __repr__ = sane_repr('team_id', 'user_id', 'type')
class UserAvatar(AvatarBase): """ A UserAvatar associates a User with their avatar photo File and contains their preferences for avatar type. """ AVATAR_TYPES = ( (0, 'letter_avatar'), (1, 'upload'), (2, 'gravatar'), ) FILE_TYPE = 'avatar.file' user = FlexibleForeignKey('sentry.User', unique=True, related_name='avatar') avatar_type = models.PositiveSmallIntegerField(default=0, choices=AVATAR_TYPES) objects = BaseManager(cache_fields=['user']) class Meta: app_label = 'sentry' db_table = 'sentry_useravatar' def get_cache_key(self, size): return 'avatar:%s:%s' % (self.user_id, size)
class TagValue(Model): """ Stores references to available filters. """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True) environment_id = BoundedPositiveIntegerField(null=True) _key = FlexibleForeignKey('tagstore.TagKey', db_column='key') value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'tagstore' unique_together = (('project_id', 'environment_id', '_key', 'value'), ) # TODO: environment index(es) index_together = (('project_id', '_key', 'last_seen'), ) __repr__ = sane_repr('project_id', 'environment_id', '_key', 'value') @property def key(self): return self._key.key def get_label(self): from sentry import tagstore return tagstore.get_tag_value_label(self.key, self.value)
class TagValue(Model): """ Stores references to available filters. """ __core__ = False project = FlexibleForeignKey('sentry.Project', null=True) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_filtervalue' unique_together = (('project', 'key', 'value'), ) __repr__ = sane_repr('project_id', 'key', 'value') def get_label(self): # HACK(dcramer): quick and dirty way to hack in better display states if self.key == 'sentry:user': return self.data.get('email') or self.value elif self.key == 'sentry:function': return '%s in %s' % (self.data['function'], self.data['filename']) elif self.key == 'sentry:filename': return self.data['filename'] return self.value def get_absolute_url(self): # HACK(dcramer): quick and dirty way to support code/users if self.key == 'sentry:user': url_name = 'sentry-user-details' elif self.key == 'sentry:filename': url_name = 'sentry-explore-code-details' elif self.key == 'sentry:function': url_name = 'sentry-explore-code-details-by-function' else: url_name = 'sentry-explore-tag-value' return absolute_uri( reverse(url_name, args=[ self.project.organization.slug, self.project.slug, self.key, self.id ])) return absolute_uri( reverse(url_name, args=[ self.project.organization.slug, self.project.slug, self.id ]))
class ApiKey(Model): organization = FlexibleForeignKey('sentry.Organization', related_name='key_set') label = models.CharField(max_length=64, blank=True, default='Default') key = models.CharField(max_length=32, unique=True) scopes = BitField(flags=( ('project:read', 'project:read'), ('project:write', 'project:write'), ('project:delete', 'project:delete'), ('team:read', 'team:read'), ('team:write', 'team:write'), ('team:delete', 'team:delete'), ('event:read', 'event:read'), ('event:write', 'event:write'), ('event:delete', 'event:delete'), ('org:read', 'org:read'), ('org:write', 'org:write'), ('org:delete', 'org:delete'), )) status = BoundedPositiveIntegerField(default=0, choices=( (ApiKeyStatus.ACTIVE, _('Active')), (ApiKeyStatus.INACTIVE, _('Inactive')), ), db_index=True) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=('key', )) class Meta: app_label = 'sentry' db_table = 'sentry_apikey' __repr__ = sane_repr('organization_id', 'key') def __unicode__(self): return six.text_type(self.key) @classmethod def generate_api_key(cls): return uuid4().hex @property def is_active(self): return self.status == ApiKeyStatus.ACTIVE def save(self, *args, **kwargs): if not self.key: self.key = ApiKey.generate_api_key() super(ApiKey, self).save(*args, **kwargs) def get_audit_log_data(self): return { 'label': self.label, 'key': self.key, 'roles': int(self.roles), 'status': self.status, }
class GroupEmailThread(Model): """ Keep track of the original Message-Id that was sent unique per email destination and Group object.This allows the tracking of proper In-Reply-To and References headers for email threading. """ __core__ = False email = models.EmailField(max_length=75) project = FlexibleForeignKey('sentry.Project', related_name="groupemail_set") group = FlexibleForeignKey('sentry.Group', related_name="groupemail_set") msgid = models.CharField(max_length=100) date = models.DateTimeField(default=timezone.now, db_index=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_groupemailthread' unique_together = ( ('email', 'group'), ('email', 'msgid'), ) __repr__ = sane_repr('email', 'group_id', 'msgid')
class ProjectBookmark(Model): """ Identifies a bookmark relationship between a user and an aggregated event (Group). """ __core__ = True project = FlexibleForeignKey(Project, blank=True, null=True, db_constraint=False) user = FlexibleForeignKey(settings.AUTH_USER_MODEL) date_added = models.DateTimeField(default=timezone.now, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_projectbookmark' unique_together = (( 'project', 'user', )) __repr__ = sane_repr('project_id', 'user_id')
class ApiToken(Model): # users can generate tokens without being key-bound key = FlexibleForeignKey('sentry.ApiKey', null=True) user = FlexibleForeignKey('sentry.User') token = models.CharField(max_length=64, unique=True) scopes = BitField(flags=( ('project:read', 'project:read'), ('project:write', 'project:write'), ('project:delete', 'project:delete'), ('team:read', 'team:read'), ('team:write', 'team:write'), ('team:delete', 'team:delete'), ('event:read', 'event:read'), ('event:write', 'event:write'), ('event:delete', 'event:delete'), ('org:read', 'org:read'), ('org:write', 'org:write'), ('org:delete', 'org:delete'), ('member:read', 'member:read'), ('member:write', 'member:write'), ('member:delete', 'member:delete'), )) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=('token', )) class Meta: app_label = 'sentry' db_table = 'sentry_apitoken' __repr__ = sane_repr('key_id', 'user_id', 'token') def __unicode__(self): return six.text_type(self.token) @classmethod def generate_token(cls): return uuid4().hex + uuid4().hex def save(self, *args, **kwargs): if not self.token: self.token = type(self).generate_token() super(ApiToken, self).save(*args, **kwargs) def get_audit_log_data(self): return { 'scopes': int(self.scopes), } def get_scopes(self): return [k for k, v in self.scopes.iteritems() if v] def has_scope(self, scope): return scope in self.scopes def get_allowed_origins(self): if self.key: return self.key.get_allowed_origins() return ()
class GroupTagValue(Model): """ Stores the total number of messages seen by a group matching the given filter. """ project = FlexibleForeignKey('sentry.Project', null=True, related_name='grouptag') group = FlexibleForeignKey('sentry.Group', related_name='grouptag') times_seen = BoundedPositiveIntegerField(default=0) key = models.CharField(max_length=MAX_TAG_KEY_LENGTH) value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_messagefiltervalue' unique_together = (('project', 'key', 'value', 'group'), ) __repr__ = sane_repr('project_id', 'group_id', 'key', 'value') def save(self, *args, **kwargs): if not self.first_seen: self.first_seen = self.last_seen super(GroupTag, self).save(*args, **kwargs) @classmethod def get_value_count(cls, group_id, key): if db.is_postgres(): # This doesnt guarantee percentage is accurate, but it does ensure # that the query has a maximum cost cursor = connections['default'].cursor() cursor.execute( """ SELECT SUM(t) FROM ( SELECT times_seen as t FROM sentry_messagefiltervalue WHERE group_id = %s AND key = %s AND last_seen > NOW() - INTERVAL '7 days' LIMIT 10000 ) as a """, [group_id, key]) return cursor.fetchone()[0] or 0 cutoff = timezone.now() - timedelta(days=7) return cls.objects.filter( group=group_id, key=key, last_seen__gte=cutoff, ).aggregate(t=Sum('times_seen'))['t']
class GroupTagValue(Model): """ Stores the total number of messages seen by a group matching the given filter. """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True) group_id = BoundedPositiveIntegerField(db_index=True) environment_id = BoundedPositiveIntegerField(null=True) times_seen = BoundedPositiveIntegerField(default=0) _key = FlexibleForeignKey('tagstore.TagKey', db_column='key_id') _value = FlexibleForeignKey('tagstore.TagValue', db_column='value_id') last_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'tagstore' unique_together = (('project_id', 'group_id', 'environment_id', '_key', '_value'), ) index_together = (('project_id', '_key', '_value', 'last_seen'), ) __repr__ = sane_repr('project_id', 'group_id', '_key', '_value') @property def key(self): return self._key.key @property def value(self): return self._value.value def save(self, *args, **kwargs): if not self.first_seen: self.first_seen = self.last_seen super(GroupTagValue, self).save(*args, **kwargs) def merge_counts(self, new_group): try: with transaction.atomic(using=router.db_for_write(GroupTagValue)): new_obj = GroupTagValue.objects.get( group_id=new_group.id, environment_id=self.environment_id, _key_id=self._key_id, _value_id=self._value_id, ) new_obj.update( first_seen=min(new_obj.first_seen, self.first_seen), last_seen=max(new_obj.last_seen, self.last_seen), times_seen=new_obj.times_seen + self.times_seen, ) except DataError: # it's possible to hit an out of range value for counters pass
class GroupTagValue(Model): """ Stores the total number of messages seen by a group matching the given filter. """ __core__ = False project_id = BoundedPositiveIntegerField(db_index=True) group_id = BoundedPositiveIntegerField(db_index=True) environment_id = BoundedPositiveIntegerField() # times_seen will live in Redis key_id = BoundedPositiveIntegerField() value_id = BoundedPositiveIntegerField() last_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField(default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'tagstore' unique_together = (('project_id', 'group_id', 'environment_id', 'key_id', 'value_id'), ) # TODO: environment index(es) index_together = (('project_id', 'key_id', 'value_id', 'last_seen'), ) __repr__ = sane_repr('project_id', 'group_id', 'key_id', 'value_id') # TODO: key property to fetch actual key string? # TODO: value property to fetch actual value string? def save(self, *args, **kwargs): if not self.first_seen: self.first_seen = self.last_seen super(GroupTagValue, self).save(*args, **kwargs) # TODO: this will have to iterate all of the possible environments a group has? # TODO: times_seen will live in Redis def merge_counts(self, new_group): try: with transaction.atomic(using=router.db_for_write(GroupTagValue)): new_obj = GroupTagValue.objects.get( group_id=new_group.id, key_id=self.key_id, value_id=self.value_id, ) new_obj.update( first_seen=min(new_obj.first_seen, self.first_seen), last_seen=max(new_obj.last_seen, self.last_seen), times_seen=new_obj.times_seen + self.times_seen, ) except DataError: # it's possible to hit an out of range value for counters pass
class ParanoidModel(Model): class Meta: abstract = True date_deleted = models.DateTimeField(null=True, blank=True) objects = ParanoidManager() with_deleted = BaseManager() def delete(self): self.update(date_deleted=timezone.now())
class ApiToken(Model, HasApiScopes): __core__ = True # users can generate tokens without being application-bound application = FlexibleForeignKey("sentry.ApiApplication", null=True) user = FlexibleForeignKey("sentry.User") token = models.CharField(max_length=64, unique=True, default=generate_token) refresh_token = models.CharField(max_length=64, unique=True, null=True, default=generate_token) expires_at = models.DateTimeField(null=True, default=default_expiration) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=("token", )) class Meta: app_label = "sentry" db_table = "sentry_apitoken" __repr__ = sane_repr("user_id", "token", "application_id") def __str__(self): return force_text(self.token) @classmethod def from_grant(cls, grant): with transaction.atomic(): return cls.objects.create(application=grant.application, user=grant.user, scope_list=grant.get_scopes()) def is_expired(self): if not self.expires_at: return False return timezone.now() >= self.expires_at def get_audit_log_data(self): return {"scopes": self.get_scopes()} def get_allowed_origins(self): if self.application: return self.application.get_allowed_origins() return () def refresh(self, expires_at=None): if expires_at is None: expires_at = timezone.now() + DEFAULT_EXPIRATION self.update(token=generate_token(), refresh_token=generate_token(), expires_at=expires_at)
class ServiceHook(Model): __core__ = True guid = models.CharField(max_length=32, unique=True, null=True) # hooks may be bound to an api application, or simply registered by a user application = FlexibleForeignKey('sentry.ApiApplication', null=True) actor_id = BoundedPositiveIntegerField(db_index=True) project_id = BoundedPositiveIntegerField(db_index=True) organization_id = BoundedPositiveIntegerField(db_index=True, null=True) url = models.URLField(max_length=512) secret = EncryptedTextField(default=generate_secret) events = ArrayField(of=models.TextField) status = BoundedPositiveIntegerField(default=0, choices=ObjectStatus.as_choices(), db_index=True) version = BoundedPositiveIntegerField(default=0, choices=((0, '0'), )) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=('guid', )) class Meta: app_label = 'sentry' db_table = 'sentry_servicehook' __repr__ = sane_repr('guid', 'project_id') @property def created_by_sentry_app(self): return (self.application_id and self.sentry_app) @property def sentry_app(self): try: return SentryApp.objects.get(application_id=self.application_id) except SentryApp.DoesNotExist: return def __init__(self, *args, **kwargs): super(ServiceHook, self).__init__(*args, **kwargs) if self.guid is None: self.guid = uuid4().hex def __unicode__(self): return six.text_type(self.guid) def build_signature(self, body): return hmac.new( key=self.secret.encode('utf-8'), msg=body.encode('utf-8'), digestmod=sha256, ).hexdigest() def get_audit_log_data(self): return {'url': self.url}
class TagValue(Model): """ Stores references to available filters. """ __core__ = False project_id = BoundedBigIntegerField(db_index=True) _key = FlexibleForeignKey('tagstore.TagKey', db_column='key_id') value = models.CharField(max_length=MAX_TAG_VALUE_LENGTH) data = GzippedDictField(blank=True, null=True) times_seen = BoundedPositiveIntegerField(default=0) last_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) first_seen = models.DateTimeField( default=timezone.now, db_index=True, null=True) objects = BaseManager() class Meta: app_label = 'tagstore' unique_together = (('project_id', '_key', 'value'), ) index_together = (('project_id', '_key', 'last_seen'), ) __repr__ = sane_repr('project_id', '_key', 'value') @property def key(self): if hasattr(self, '_set_key'): return self._set_key if hasattr(self, '__key_cache'): return self._key.key # fallback from sentry.tagstore.v2.models import TagKey tk = TagKey.objects.filter( project_id=self.project_id, id=self._key_id, ).values_list('key', flat=True).get() # cache for future calls self.key = tk return tk @key.setter def key(self, key): self._set_key = key def get_label(self): from sentry import tagstore return tagstore.get_tag_value_label(self.key, self.value)
class PendingTeamMember(Model): """ Identifies relationships between teams and pending invites. """ team = FlexibleForeignKey('sentry.Team', related_name="pending_member_set") email = models.EmailField() type = BoundedIntegerField(choices=MEMBER_TYPES, default=MEMBER_USER) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager() class Meta: app_label = 'sentry' db_table = 'sentry_pendingteammember' unique_together = (('team', 'email'), ) __repr__ = sane_repr('team_id', 'email', 'type') @property def token(self): checksum = md5() for x in (str(self.team_id), self.email, settings.SECRET_KEY): checksum.update(x) return checksum.hexdigest() def send_invite_email(self): from sentry.utils.email import MessageBuilder context = { 'email': self.email, 'team': self.team, 'url': absolute_uri( reverse('sentry-accept-invite', kwargs={ 'member_id': self.id, 'token': self.token, })), } msg = MessageBuilder( subject='Invite to join team: %s' % (self.team.name, ), template='sentry/emails/member_invite.txt', context=context, ) try: msg.send([self.email]) except Exception as e: logger = logging.getLogger('sentry.mail.errors') logger.exception(e)
class MonitorLocation(Model): __core__ = True guid = UUIDField(unique=True, auto_add=True) name = models.CharField(max_length=128) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=('guid', )) class Meta: app_label = 'sentry' db_table = 'sentry_monitorlocation' __repr__ = sane_repr('guid', 'name')
class MonitorLocation(Model): __include_in_export__ = True guid = UUIDField(unique=True, auto_add=True) name = models.CharField(max_length=128) date_added = models.DateTimeField(default=timezone.now) objects = BaseManager(cache_fields=("guid", )) class Meta: app_label = "sentry" db_table = "sentry_monitorlocation" __repr__ = sane_repr("guid", "name")
class GroupTagKey(Model): """ Stores a unique tag key name for a group. An example key might be "url" or "server_name". """ __core__ = False project_id = BoundedBigIntegerField(db_index=True) group_id = BoundedBigIntegerField(db_index=True) _key = FlexibleForeignKey('tagstore.TagKey', db_column='key_id') values_seen = BoundedPositiveIntegerField(default=0) objects = BaseManager() class Meta: app_label = 'tagstore' unique_together = (('project_id', 'group_id', '_key'), ) __repr__ = sane_repr('project_id', 'group_id', '_key') @property def key(self): if hasattr(self, '_set_key'): return self._set_key return self._key.key @key.setter def key(self, key): self._set_key = key @staticmethod def get_select_related_for_merge(): return ('_key', ) def merge_counts(self, new_group): from sentry.tagstore.v2.models import GroupTagValue try: with transaction.atomic(using=router.db_for_write(GroupTagKey)): GroupTagKey.objects.filter( group_id=new_group.id, _key_id=self._key_id, ).update(values_seen=GroupTagValue.objects.filter( group_id=new_group.id, _key_id=self._key_id, ).count()) except DataError: # it's possible to hit an out of range value for counters pass
class VersionDSymFile(Model): __core__ = False objects = BaseManager() dsym_file = FlexibleForeignKey('sentry.ProjectDSymFile', null=True) dsym_app = FlexibleForeignKey('sentry.DSymApp') version = models.CharField(max_length=32) build = models.CharField(max_length=32, null=True) date_added = models.DateTimeField(default=timezone.now) class Meta: app_label = 'sentry' db_table = 'sentry_versiondsymfile' unique_together = (('dsym_file', 'version', 'build'), )
def create_or_update_app( self, sync_id, app_id, project, data=None, platform=DSymPlatform.GENERIC, no_fetch=False ): if data is None: data = {} if not no_fetch: _auto_enrich_data(data, app_id, platform) existing_app = DSymApp.objects.filter(app_id=app_id, project=project).first() if existing_app is not None: now = timezone.now() existing_app.update( sync_id=sync_id, data=data, last_synced=now, ) return existing_app return BaseManager.create( self, sync_id=sync_id, app_id=app_id, data=data, project=project, platform=platform )