def _get_event_user(self, project, data): user_data = data.get("sentry.interfaces.User") if not user_data: return euser = EventUser( project=project, ident=user_data.get("id"), email=user_data.get("email"), username=user_data.get("username"), ip_address=user_data.get("ip_address"), ) if not euser.tag_value: return cache_key = "euser:{}:{}".format(project.id, md5(euser.tag_value.encode("utf-8")).hexdigest()) cached = default_cache.get(cache_key) if cached is None: try: with transaction.atomic(using=router.db_for_write(EventUser)): euser.save() except IntegrityError: pass default_cache.set(cache_key, "", 3600) return euser
def record_affected_user(event, **kwargs): from sentry.models import EventUser, Group user_data = event.data.get('sentry.interfaces.User', event.data.get('user')) if not user_data: logger.info('No user data found for event_id=%s', event.event_id) return euser = EventUser( project=event.project, ident=user_data.get('id'), email=user_data.get('email'), username=user_data.get('username'), ip_address=user_data.get('ip_address'), ) if not euser.tag_value: # no ident, bail logger.info('No identifying value found for user on event_id=%s', event.event_id) return try: with transaction.atomic(): euser.save() except IntegrityError: pass Group.objects.add_tags(event.group, [ ('sentry:user', euser.tag_value) ])
def _get_event_user(self, project, data): user_data = data.get('sentry.interfaces.User') if not user_data: return euser = EventUser( project=project, ident=user_data.get('id'), email=user_data.get('email'), username=user_data.get('username'), ip_address=user_data.get('ip_address'), ) if not euser.tag_value: return cache_key = 'euser:{}:{}'.format( project.id, md5_text(euser.tag_value).hexdigest(), ) cached = default_cache.get(cache_key) if cached is None: try: with transaction.atomic(using=router.db_for_write(EventUser)): euser.save() except IntegrityError: pass default_cache.set(cache_key, '', 3600) return euser
def test_get_group_tag_values_for_users(self): result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident='user1')]) one_second_ago = ( self.now - timedelta(seconds=1)).strftime('%Y-%m-%dT%H:%M:%S+00:00') assert len(result) == 1 assert result[0].value == 'user1' assert result[0].last_seen == one_second_ago result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident='user2')]) two_seconds_ago = ( self.now - timedelta(seconds=2)).strftime('%Y-%m-%dT%H:%M:%S+00:00') assert len(result) == 1 assert result[0].value == 'user2' assert result[0].last_seen == two_seconds_ago # Test that users identified by different means are collected. # (effectively tests OR conditions in snuba API) result = self.ts.get_group_tag_values_for_users([ EventUser(project_id=self.proj1.id, email='*****@*****.**'), EventUser(project_id=self.proj1.id, ident='user2') ]) two_seconds_ago = ( self.now - timedelta(seconds=2)).strftime('%Y-%m-%dT%H:%M:%S+00:00') assert len(result) == 2 result.sort(key=lambda x: x.value) assert result[0].value == 'user1' assert result[0].last_seen == one_second_ago assert result[1].value == 'user2' assert result[1].last_seen == two_seconds_ago
def test_for_tags(self): eu = EventUser.objects.create(project_id=1, ident="matt") assert EventUser.for_tags(1, ["id:matt"]) == {"id:matt": eu} assert EventUser.for_tags(1, ["id:doesnotexist"]) == {} assert EventUser.for_tags(1, ["id:matt", "id:doesnotexist"]) == { "id:matt": eu }
def record_affected_user(event, **kwargs): from sentry.models import EventUser, Group Raven.tags_context({ 'project': event.project_id, }) user_data = event.data.get('sentry.interfaces.User', event.data.get('user')) if not user_data: logger.info('No user data found for event_id=%s', event.event_id) return euser = EventUser( project=event.project, ident=user_data.get('id'), email=user_data.get('email'), username=user_data.get('username'), ip_address=user_data.get('ip_address'), ) if not euser.tag_value: # no ident, bail logger.info('No identifying value found for user on event_id=%s', event.event_id) return try: with transaction.atomic(using=router.db_for_write(EventUser)): euser.save() except IntegrityError: pass Group.objects.add_tags(event.group, [('sentry:user', euser.tag_value)])
def test_get_group_tag_values_for_users(self): result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident='user1')]) assert len(result) == 2 assert set(v.group_id for v in result) == set([ self.proj1group1.id, self.proj1group2.id, ]) assert set(v.last_seen for v in result) == \ set([self.now - timedelta(seconds=1), self.now - timedelta(seconds=2)]) result.sort(key=lambda x: x.last_seen) assert result[0].last_seen == self.now - timedelta(seconds=2) assert result[1].last_seen == self.now - timedelta(seconds=1) for v in result: assert v.value == 'user1' result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident='user2')]) assert len(result) == 1 assert result[0].value == 'user2' assert result[0].last_seen == self.now - timedelta(seconds=2) # Test that users identified by different means are collected. # (effectively tests OR conditions in snuba API) result = self.ts.get_group_tag_values_for_users([ EventUser(project_id=self.proj1.id, email='*****@*****.**'), EventUser(project_id=self.proj1.id, ident='user2') ]) assert len(result) == 2 result.sort(key=lambda x: x.value) assert result[0].value == 'user1' assert result[0].last_seen == self.now - timedelta(seconds=1) assert result[1].value == 'user2' assert result[1].last_seen == self.now - timedelta(seconds=2)
def test_get_group_ids_for_users(self): assert self.ts.get_group_ids_for_users( [self.proj1.id], [EventUser(project_id=self.proj1.id, ident="user1")] ) == set([self.proj1group1.id, self.proj1group2.id]) assert self.ts.get_group_ids_for_users( [self.proj1.id], [EventUser(project_id=self.proj1.id, ident="user2")] ) == set([self.proj1group1.id])
def test_for_tags(self): eu = EventUser.objects.create( project_id=1, ident='matt', ) assert EventUser.for_tags(1, ['id:matt']) == {'id:matt': eu} assert EventUser.for_tags(1, ['id:doesnotexist']) == {} assert EventUser.for_tags(1, ['id:matt', 'id:doesnotexist']) == {'id:matt': eu}
def get(self, request, project): """ List a Project's Users `````````````````````` Return a list of users seen within this project. :pparam string organization_slug: the slug of the organization. :pparam string project_slug: the slug of the project. :pparam string key: the tag key to look up. :auth: required :qparam string query: Limit results to users matching the given query. Prefixes should be used to suggest the field to match on: ``id``, ``email``, ``username``, ``ip``. For example, ``query=email:[email protected]`` """ queryset = EventUser.objects.filter(project_id=project.id) if request.GET.get("query"): try: field, identifier = request.GET["query"].strip().split(":", 1) except ValueError: return Response([]) # username and ip can return multiple eventuser objects if field in ("ip", "username"): queryset = queryset.filter( project_id=project.id, **{EventUser.attr_from_keyword(field): identifier}, ) else: try: queryset = [ queryset.get( project_id=project.id, **{EventUser.attr_from_keyword(field): identifier}, ) ] except EventUser.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) except KeyError: return Response([]) return Response(serialize(queryset, request.user)) return self.paginate( request=request, queryset=queryset, order_by="-date_added", paginator_cls=DateTimePaginator, on_results=lambda x: serialize(x, request.user), )
def get_attrs(self, item_list, user): users = EventUser.for_tags(project_id=item_list[0].project_id, values=[t.value for t in item_list]) result = {} for item in item_list: result[item] = {"user": users.get(item.value)} return result
def test_build_hash(self): cases = [ ( { "ident": "ident", "username": "******", "email": "email", "ip_address": "127.0.0.1", }, "67217d8b401cf5e72bbf5103d60f3e97", ), ( { "username": "******", "email": "email", "ip_address": "127.0.0.1" }, "14c4b06b824ec593239362517f538b29", ), ({ "email": "email", "ip_address": "127.0.0.1" }, "0c83f57c786a0b4a39efab23731c7ebc"), ({ "ip_address": "127.0.0.1" }, "f528764d624db129b32c21fbca0cb8d6"), ({}, None), ] for kw, value in cases: assert EventUser(**kw).build_hash() == value
def test_tag_value(self): cases = [ ( { "ident": "ident", "username": "******", "email": "email", "ip_address": "127.0.0.1", }, "id:ident", ), ( { "username": "******", "email": "email", "ip_address": "127.0.0.1" }, "username:username", ), ({ "email": "email", "ip_address": "127.0.0.1" }, "email:email"), ({ "ip_address": "127.0.0.1" }, "ip:127.0.0.1"), ({}, None), ] for kw, value in cases: assert EventUser(**kw).tag_value == value
def find_event_user(self, report): try: event = Event.objects.get( group_id=report.group_id, event_id=report.event_id, ) except Event.DoesNotExist: if not report.email: return None try: return EventUser.objects.filter( project=report.project_id, email=report.email, )[0] except IndexError: return None tag = event.get_tag('sentry:user') if not tag: return None try: return EventUser.for_tags( project_id=report.project_id, values=[tag], )[tag] except KeyError: pass
def find_event_user(self, report): try: event = Event.objects.get( group_id=report.group_id, event_id=report.event_id, ) except Event.DoesNotExist: if not report.email: return None try: return EventUser.objects.filter( project_id=report.project_id, email=report.email, )[0] except IndexError: return None tag = event.get_tag('sentry:user') if not tag: return None try: return EventUser.for_tags( project_id=report.project_id, values=[tag], )[tag] except KeyError: pass
def find_event_user(self, report_data): try: event = Event.objects.get( group_id=report_data.get('group_id'), event_id=report_data['event_id'], ) except Event.DoesNotExist: if not report_data.get('email'): return None try: return EventUser.objects.filter( project_id=report_data['project'].id, email=report_data['email'], )[0] except IndexError: return None tag = event.get_tag('sentry:user') if not tag: return None try: return EventUser.for_tags( project_id=report_data['project'].id, values=[tag], )[tag] except KeyError: pass
def get_event_user_from_interface(value): return EventUser( ident=value.get("id"), email=value.get("email"), username=value.get("valuename"), ip_address=value.get("ip_address"), )
def get(self, request, project): """ List a Project's Users `````````````````````` Return a list of users seen within this project. :pparam string organization_slug: the slug of the organization. :pparam string project_slug: the slug of the project. :pparam string key: the tag key to look up. :auth: required :qparam string query: Limit results to users matching the given query. Prefixes should be used to suggest the field to match on: ``id``, ``email``, ``username``, ``ip``. For example, ``query=email:[email protected]`` """ queryset = EventUser.objects.filter(project_id=project.id) if request.GET.get("query"): pieces = request.GET["query"].strip().split(":", 1) if len(pieces) != 2: return Response([]) try: queryset = queryset.filter( **{u"{}__icontains".format(EventUser.attr_from_keyword(pieces[0])): pieces[1]} ) except KeyError: return Response([]) return self.paginate( request=request, queryset=queryset, order_by="-date_added", paginator_cls=DateTimePaginator, on_results=lambda x: serialize(x, request.user), )
def get_event_user_from_interface(value): return EventUser( ident=value.get('id'), email=value.get('email'), username=value.get('valuename'), ip_address=value.get('ip_address'), )
def get_attrs(self, item_list, user): user_tags = [i.value for i in item_list if i.key == 'sentry:user'] tag_labels = {} if user_tags: tag_labels.update( { ('sentry:user', k): v.get_label() for k, v in six.iteritems( EventUser.for_tags( project_id=item_list[0].project_id, values=user_tags, ) ) } ) result = {} for item in item_list: try: label = tag_labels[(item.key, item.value)] except KeyError: label = item.get_label() result[item] = { 'name': label, } return result
def get_attrs(self, item_list, user): users = EventUser.for_tags(project_id=self.project_id, values=[t.value for t in item_list]) result = {} for item in item_list: result[item] = {"user": users.get(item.value)} return result
def get_user_tag(project, key, value): # TODO(dcramer): do something with case of multiple matches try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter(project=project, **{lookup: value})[0] except (KeyError, IndexError): return '{}:{}'.format(key, value) return euser.tag_value
def test_attr_from_keyword(self): cases = [ ('id', 'ident'), ('username', 'username'), ('email', 'email'), ('ip', 'ip_address'), ] for keyword, attr in cases: assert EventUser.attr_from_keyword(keyword) == attr
def test_attr_from_keyword(self): cases = [ ("id", "ident"), ("username", "username"), ("email", "email"), ("ip", "ip_address"), ] for keyword, attr in cases: assert EventUser.attr_from_keyword(keyword) == attr
def get_user_tag(project, key, value): # TODO(dcramer): do something with case of multiple matches try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter(project=project, **{lookup: value})[0] except (KeyError, IndexError): return "{}:{}".format(key, value) return euser.tag_value
def _get_event_user_impl(project, data, metrics_tags): user_data = data.get("user") if not user_data: metrics_tags["event_has_user"] = "******" return metrics_tags["event_has_user"] = "******" ip_address = user_data.get("ip_address") if ip_address: try: ipaddress.ip_address(six.text_type(ip_address)) except ValueError: ip_address = None euser = EventUser( project_id=project.id, ident=user_data.get("id"), email=user_data.get("email"), username=user_data.get("username"), ip_address=ip_address, name=user_data.get("name"), ) euser.set_hash() if not euser.hash: return cache_key = u"euserid:1:{}:{}".format(project.id, euser.hash) euser_id = cache.get(cache_key) if euser_id is None: metrics_tags["cache_hit"] = "false" try: with transaction.atomic(using=router.db_for_write(EventUser)): euser.save() metrics_tags["created"] = "true" except IntegrityError: metrics_tags["created"] = "false" try: euser = EventUser.objects.get(project_id=project.id, hash=euser.hash) except EventUser.DoesNotExist: metrics_tags["created"] = "lol" # why??? e_userid = -1 else: if euser.name != (user_data.get("name") or euser.name): euser.update(name=user_data["name"]) e_userid = euser.id cache.set(cache_key, e_userid, 3600) else: metrics_tags["cache_hit"] = "true" return euser
def serialize_eventusers(organization, item_list, user, lookup): if not item_list: return {} # We have no reliable way to map the tag value format # back into real EventUser rows. EventUser is only unique # per-project, and this is an organization aggregate. # This means a single value maps to multiple rows. filters = reduce(or_, [ Q(hash=EventUser.hash_from_tag(tag), project_id=project) for tag, project in item_list ]) eu_by_key = { (eu.tag_value, eu.project_id): eu for eu in EventUser.objects.filter(filters) } projects = serialize_projects(organization, {i[1] for i in item_list}, user) rv = {} for tag, project in item_list: eu = eu_by_key.get((tag, project)) if eu is None: attr, value = tag.split(':', 1) eu = EventUser(project_id=project, **{EventUser.attr_from_keyword(attr): value}) rv[(tag, project)] = { HEALTH_ID_KEY: make_health_id(lookup, [eu.tag_value, eu.project_id]), 'value': { 'id': six.text_type(eu.id) if eu.id else None, 'project': projects.get(eu.project_id), 'hash': eu.hash, 'tagValue': eu.tag_value, 'identifier': eu.ident, 'username': eu.username, 'email': eu.email, 'ipAddress': eu.ip_address, 'dateCreated': eu.date_added, 'label': eu.get_label(), 'name': eu.get_display_name(), 'geo': geo_by_addr(eu.ip_address), }, } return rv
def get_user_tag(project, key, value): # TODO(dcramer): do something with case of multiple matches try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter(project=project, **{lookup: value})[0] except (KeyError, IndexError): return u'{}:{}'.format(key, value) except DataError: raise InvalidQuery(u"malformed '{}:' query '{}'.".format(key, value)) return euser.tag_value
def test_get_group_tag_values_for_users(self): result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident="user1")]) assert len(result) == 2 assert set(v.group_id for v in result) == set( [self.proj1group1.id, self.proj1group2.id]) assert set(v.last_seen for v in result) == set( [self.now - timedelta(seconds=1), self.now - timedelta(seconds=2)]) result.sort(key=lambda x: x.last_seen) assert result[0].last_seen == self.now - timedelta(seconds=2) assert result[1].last_seen == self.now - timedelta(seconds=1) for v in result: assert v.value == "user1" result = self.ts.get_group_tag_values_for_users( [EventUser(project_id=self.proj1.id, ident="user2")]) assert len(result) == 1 assert result[0].value == "user2" assert result[0].last_seen == self.now - timedelta(seconds=2)
def get_user_tag(project, key, value): # TODO(dcramer): do something with case of multiple matches try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter( project_id=project.id, **{lookup: value})[0] except (KeyError, IndexError): return u'{}:{}'.format(key, value) except DataError: raise InvalidQuery(u"malformed '{}:' query '{}'.".format(key, value)) return euser.tag_value
def test_get_group_tag_values_for_users(self): from sentry.models import EventUser v1, _ = self.ts.get_or_create_group_tag_value(self.proj1.id, self.proj1group1.id, None, 'sentry:user', 'email:[email protected]') eu = EventUser(project_id=self.proj1.id, email='*****@*****.**') assert self.ts.get_group_tag_values_for_users([eu]) == [v1]
def get_user_tag(projects, key, value): # TODO(dcramer): do something with case of multiple matches try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter( project_id__in=[p.id for p in projects], **{lookup: value})[0] except (KeyError, IndexError): return f"{key}:{value}" except DataError: raise InvalidQuery(f"malformed '{key}:' query '{value}'.") return euser.tag_value
def test_get_group_tag_values_for_users(self): from sentry.models import EventUser v1, _ = self.ts.get_or_create_group_tag_value(self.proj1.id, self.proj1group1.id, 0, "sentry:user", "email:[email protected]") eu = EventUser(project_id=self.proj1.id, email="*****@*****.**") assert self.ts.get_group_tag_values_for_users( [eu]) == [transformers[models.GroupTagValue](v1)]
def test_build_hash(self): cases = [ ({'ident': 'ident', 'username': '******', 'email': 'email', 'ip_address': '127.0.0.1'}, '67217d8b401cf5e72bbf5103d60f3e97'), ({'username': '******', 'email': 'email', 'ip_address': '127.0.0.1'}, '14c4b06b824ec593239362517f538b29'), ({'email': 'email', 'ip_address': '127.0.0.1'}, '0c83f57c786a0b4a39efab23731c7ebc'), ({'ip_address': '127.0.0.1'}, 'f528764d624db129b32c21fbca0cb8d6'), ({}, None), ] for kw, value in cases: assert EventUser(**kw).build_hash() == value
def get_user_tag(projects, key, value): # TODO(dcramer): do something with case of multiple matches from sentry.models import EventUser # Django 1.9 setup issue try: lookup = EventUser.attr_from_keyword(key) euser = EventUser.objects.filter( project_id__in=[p.id for p in projects], **{lookup: value})[0] except (KeyError, IndexError): return u'{}:{}'.format(key, value) except DataError: raise InvalidQuery(u"malformed '{}:' query '{}'.".format(key, value)) return euser.tag_value
def get_attrs(self, item_list, user): users = EventUser.for_tags( project_id=item_list[0].project_id, values=[t.value for t in item_list], ) result = {} for item in item_list: result[item] = { 'user': users.get(item.value), } return result
def get_attrs(self, item_list, user): users = EventUser.for_tags( project_id=self.project_id, values=[t.value for t in item_list], ) result = {} for item in item_list: result[item] = { 'user': users.get(item.value), } return result
def test_get_group_ids_for_users(self): from sentry.models import EventUser v1, _ = self.ts.get_or_create_group_tag_value(self.proj1.id, self.proj1group1.id, 0, "sentry:user", "email:[email protected]") eu = EventUser(project_id=self.proj1.id, email="*****@*****.**") assert self.ts.get_group_ids_for_users([self.proj1.id], [eu]) == set( [self.proj1group1.id])
def _get_event_user(self, project, data): user_data = data.get('sentry.interfaces.User') if not user_data: return euser = EventUser( project=project, ident=user_data.get('id'), email=user_data.get('email'), username=user_data.get('username'), ip_address=user_data.get('ip_address'), ) if not euser.tag_value: return try: with transaction.atomic(): euser.save() except IntegrityError: pass return euser
def get_attrs(self, item_list, user): user_tags = [i.value for i in item_list if i.key == "sentry:user"] tag_labels = {} if user_tags: tag_labels.update( { ("sentry:user", k): v.get_label() for k, v in six.iteritems(EventUser.for_tags(project_id=item_list[0].project_id, values=user_tags)) } ) result = {} for item in item_list: try: label = tag_labels[(item.key, item.value)] except KeyError: label = item.get_label() result[item] = {"name": label} return result
def _get_event_user(self, project, data): user_data = data.get('sentry.interfaces.User') if not user_data: return euser = EventUser( project_id=project.id, ident=user_data.get('id'), email=user_data.get('email'), username=user_data.get('username'), ip_address=user_data.get('ip_address'), name=user_data.get('name'), ) euser.set_hash() if not euser.hash: return cache_key = 'euserid:1:{}:{}'.format( project.id, euser.hash, ) euser_id = default_cache.get(cache_key) if euser_id is None: try: with transaction.atomic(using=router.db_for_write(EventUser)): euser.save() except IntegrityError: try: euser = EventUser.objects.get( project_id=project.id, hash=euser.hash, ) except EventUser.DoesNotExist: # why??? e_userid = -1 else: if euser.name != (user_data.get('name') or euser.name): euser.update( name=user_data['name'], ) e_userid = euser.id default_cache.set(cache_key, e_userid, 3600) return euser
def get(self, request, project): """ List a Project's Users `````````````````````` Return a list of users seen within this project. :pparam string organization_slug: the slug of the organization. :pparam string project_slug: the slug of the project. :pparam string key: the tag key to look up. :auth: required :qparam string query: Limit results to users matching the given query. Prefixes should be used to suggest the field to match on: ``id``, ``email``, ``username``, ``ip``. For example, ``query=email:[email protected]`` """ queryset = EventUser.objects.filter( project_id=project.id, ) if request.GET.get('query'): pieces = request.GET['query'].strip().split(':', 1) if len(pieces) != 2: return Response([]) try: queryset = queryset.filter( **{u'{}__icontains'.format(EventUser.attr_from_keyword(pieces[0])): pieces[1]} ) except KeyError: return Response([]) return self.paginate( request=request, queryset=queryset, order_by='-date_added', paginator_cls=DateTimePaginator, on_results=lambda x: serialize(x, request.user), )
def test_hash_from_tag(self): assert EventUser.hash_from_tag('foo:bar:baz') == md5('bar:baz').hexdigest()
def wrapped(items): users = EventUser.for_tags(project_id, [i.value for i in items]) for item in items: item._eventuser = users.get(item.value)