def get_group_tag_keys_and_top_values(self, project_id, group_id, environment_id, user=None): from sentry import tagstore start, end = self.get_time_range() filters = { 'project_id': [project_id], 'environment': [environment_id], 'issue': [group_id], } aggregations = [ ['topK(10)', 'tags.value', 'top'], ['count', '', 'count'], ['uniq', 'tags.key', 'uniq'], ] results = snuba.query(start, end, ['tags.key'], None, filters, aggregations, arrayjoin='tags') return [{ 'id': key, 'name': tagstore.get_tag_key_label(key), 'key': tagstore.get_standardized_key(key), 'uniqueValues': res['uniq'], 'totalValues': res['count'], 'topValues': [{ 'id': val, 'name': tagstore.get_tag_value_label(key, val), 'key': tagstore.get_standardized_key(key), 'value': val, } for val in res['top']], } for key, res in six.iteritems(results)]
def get_group_tag_keys_and_top_values(self, project_id, group_id, environment_id, user=None): from sentry import tagstore start, end = self.get_time_range() filters = { 'project_id': [project_id], 'environment': [environment_id], 'issue': [group_id], } aggregations = [ ['count()', '', 'count'], ['topK(10)', 'tags_value', 'top'], ['uniq', 'tags_value', 'uniq'], ] conditions = [ ['tags_value', 'IS NOT NULL', None], ] results = snuba.query(start, end, ['tags_key'], conditions, filters, aggregations) return [{ 'id': key, 'name': tagstore.get_tag_key_label(key), 'key': tagstore.get_standardized_key(key), 'uniqueValues': res['uniq'], 'totalValues': res['count'], 'topValues': [{ 'id': val, 'name': tagstore.get_tag_value_label(key, val), 'key': tagstore.get_standardized_key(key), 'value': val, } for val in res['top']], } for key, res in six.iteritems(results)]
def get(self, request, project): try: environment_id = self._get_environment_id_from_request(request, project.organization_id) except Environment.DoesNotExist: tag_keys = [] else: tag_keys = sorted( tagstore.get_tag_keys( project.id, environment_id, ), key=lambda x: x.key) data = [] for tag_key in tag_keys: data.append( { 'key': tagstore.get_standardized_key(tag_key.key), 'name': tagstore.get_tag_key_label(tag_key.key), 'uniqueValues': tag_key.values_seen, 'canDelete': tag_key.key not in PROTECTED_TAG_KEYS, } ) return Response(data)
def serialize(self, obj, attrs, user): return { 'id': six.text_type(obj.id), 'key': tagstore.get_standardized_key(obj.key), 'name': obj.get_label(), 'uniqueValues': obj.values_seen, }
def get(self, request, group): group_tag_keys = tagstore.get_group_tag_keys(group.id) # O(N) db access data = [] all_top_values = [] for group_tag_key in group_tag_keys: total_values = tagstore.get_group_tag_value_count(group.id, group_tag_key.key) top_values = tagstore.get_top_group_tag_values(group.id, group_tag_key.key, limit=10) all_top_values.extend(top_values) data.append( { 'id': six.text_type(group_tag_key.id), 'key': tagstore.get_standardized_key(group_tag_key.key), 'name': tagstore.get_tag_key_label(group_tag_key.key), 'uniqueValues': group_tag_key.values_seen, 'totalValues': total_values, } ) # Serialize all of the values at once to avoid O(n) serialize/db queries top_values_by_key = defaultdict(list) for value in serialize(all_top_values, request.user): top_values_by_key[value['key']].append(value) for d in data: d['topValues'] = top_values_by_key[d['key']] return Response(data)
def data_fn(offset, limit): with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"): with self.handle_query_errors(): facets = discover.get_performance_facets( query=request.GET.get("query"), params=params, referrer="api.organization-events-facets-performance.top-tags", aggregate_column=aggregate_column, orderby=orderby, offset=offset, limit=limit, ) with sentry_sdk.start_span( op="discover.endpoint", description="populate_results" ) as span: span.set_data("facet_count", len(facets or [])) resp = defaultdict(lambda: {"key": "", "value": {}}) for row in facets: values = resp[row.key] values["key"] = tagstore.get_standardized_key(row.key) values["value"] = { "name": tagstore.get_tag_value_label(row.key, row.value), "value": row.value, "count": row.count, "frequency": row.frequency, "aggregate": row.performance, "comparison": row.comparison, "sumdelta": row.sumdelta, } return {"data": list(resp.values())}
def build_tags_widget(self, project, event): if not self.get_option("include_tags", project): return None tags = [] included_tags = set( self.get_tag_list("included_tag_keys", project) or []) excluded_tags = set( self.get_tag_list("excluded_tag_keys", project) or []) for tag_key, tag_value in self._get_tags(event): key = tag_key.lower() std_key = tagstore.get_standardized_key(key) if included_tags and key not in included_tags and std_key not in included_tags: continue if excluded_tags and (key in excluded_tags or std_key in excluded_tags): continue tags.append({ "keyValue": { "topLabel": tag_key.encode("utf-8"), "content": tag_value.encode("utf-8") } }) return tags
def get(self, request, project): try: environment_id = self._get_environment_id_from_request(request, project.organization_id) except Environment.DoesNotExist: tag_keys = [] else: tag_keys = sorted( tagstore.get_tag_keys( project.id, environment_id, # We might be able to stop including these values, but this # is a pretty old endpoint, so concerned about breaking # existing api consumers. include_values_seen=True, ), key=lambda x: x.key) data = [] for tag_key in tag_keys: data.append( { 'key': tagstore.get_standardized_key(tag_key.key), 'name': tagstore.get_tag_key_label(tag_key.key), 'uniqueValues': tag_key.values_seen, 'canDelete': tag_key.key not in PROTECTED_TAG_KEYS, } ) return Response(data)
def get(self, request, group): grouptagkeys = list( GroupTagKey.objects.filter(group_id=group.id).values_list( 'key', flat=True)) tag_keys = tagstore.get_tag_keys(group.project_id, grouptagkeys) # O(N) db access data = [] all_top_values = [] for tag_key in tag_keys: total_values = GroupTagValue.get_value_count(group.id, tag_key.key) top_values = GroupTagValue.get_top_values(group.id, tag_key.key, limit=10) all_top_values.extend(top_values) data.append({ 'id': six.text_type(tag_key.id), 'key': tagstore.get_standardized_key(tag_key.key), 'name': tag_key.get_label(), 'uniqueValues': tag_key.values_seen, 'totalValues': total_values, }) # Serialize all of the values at once to avoid O(n) serialize/db queries top_values_by_key = defaultdict(list) for value in serialize(all_top_values, request.user): top_values_by_key[value['key']].append(value) for d in data: d['topValues'] = top_values_by_key[d['key']] return Response(data)
def get(self, request, project): try: environment_id = self._get_environment_id_from_request( request, project.organization_id) except Environment.DoesNotExist: tag_keys = [] else: tag_keys = sorted( tagstore.get_tag_keys( project.id, environment_id, # We might be able to stop including these values, but this # is a pretty old endpoint, so concerned about breaking # existing api consumers. include_values_seen=True, ), key=lambda x: x.key) data = [] for tag_key in tag_keys: data.append({ 'key': tagstore.get_standardized_key(tag_key.key), 'name': tagstore.get_tag_key_label(tag_key.key), 'uniqueValues': tag_key.values_seen, 'canDelete': tag_key.key not in PROTECTED_TAG_KEYS, }) return Response(data)
def get(self, request, project): try: environment_id = self._get_environment_id_from_request(request, project.organization_id) except Environment.DoesNotExist: tag_keys = [] else: tag_keys = sorted( tagstore.get_tag_keys( project.id, environment_id, ), key=lambda x: x.key) data = [] for tag_key in tag_keys: data.append( { 'id': six.text_type(tag_key.id), 'key': tagstore.get_standardized_key(tag_key.key), 'name': tagstore.get_tag_key_label(tag_key.key), 'uniqueValues': tag_key.values_seen, 'canDelete': tag_key.key not in PROTECTED_TAG_KEYS, } ) return Response(data)
def data_fn(offset, limit): with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"): referrer = "api.organization-events-facets-performance-histogram.top-tags" tag_data = query_tag_data( filter_query=filter_query, aggregate_column=aggregate_column, referrer=referrer, params=params, ) if not tag_data: return {"data": []} results = query_facet_performance_key_histogram( tag_data=tag_data, tag_key=tag_key, filter_query=filter_query, aggregate_column=aggregate_column, referrer=referrer, orderby=self.get_orderby(request), params=params, limit=limit, ) if not results: return {"data": []} for row in results["data"]: row["tags_value"] = tagstore.get_tag_value_label( row["tags_key"], row["tags_value"]) row["tags_key"] = tagstore.get_standardized_key( row["tags_key"]) return results
def get(self, request, organization): with sentry_sdk.start_span(op="discover.endpoint", description="filter_params") as span: span.set_data("organization", organization) if not features.has("organizations:discover-basic", organization, actor=request.user): return Response(status=404) try: params = self.get_filter_params(request, organization) except NoProjects: return Response([]) params = self.quantize_date_params(request, params) self._validate_project_ids(request, organization, params) with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"): try: facets = discover.get_facets( query=request.GET.get("query"), params=params, referrer="api.organization-events-facets.top-tags", ) except (discover.InvalidSearchQuery, snuba.QueryOutsideRetentionError) as error: raise ParseError(detail=six.text_type(error)) with sentry_sdk.start_span(op="discover.endpoint", description="populate_results") as span: span.set_data("facet_count", len(facets or [])) resp = defaultdict(lambda: {"key": "", "topValues": []}) for row in facets: values = resp[row.key] values["key"] = tagstore.get_standardized_key(row.key) values["topValues"].append({ "name": tagstore.get_tag_value_label(row.key, row.value), "value": row.value, "count": row.count, }) if "project" in resp: # Replace project ids with slugs as that is what we generally expose to users # and filter out projects that the user doesn't have access too. projects = { p.id: p.slug for p in self.get_projects(request, organization) } filtered_values = [] for v in resp["project"]["topValues"]: if v["value"] in projects: name = projects[v["value"]] v.update({"name": name}) filtered_values.append(v) resp["project"]["topValues"] = filtered_values return Response(resp.values())
def serialize(self, obj, attrs, user): from sentry import tagstore return { 'key': tagstore.get_standardized_key(obj.key), 'name': tagstore.get_tag_key_label(obj.key), 'uniqueValues': obj.values_seen, }
def get_attrs(self, item_list, user): result = {} for item in item_list: key = tagstore.get_standardized_key(item.key) result[item] = { 'name': tagstore.get_tag_key_label(item.key), 'key': key, } return result
def serialize(self, obj, attrs, user): return { 'id': six.text_type(obj.id), 'name': attrs['name'], 'key': tagstore.get_standardized_key(obj.key), 'value': obj.value, 'count': obj.times_seen, 'lastSeen': obj.last_seen, 'firstSeen': obj.first_seen, }
def get(self, request, organization): if not features.has("organizations:discover-basic", organization, actor=request.user): return Response(status=404) try: params = self.get_filter_params(request, organization) except OrganizationEventsError as error: raise ParseError(detail=six.text_type(error)) except NoProjects: return Response({"detail": "A valid project must be included."}, status=400) try: self._validate_project_ids(request, organization, params) except OrganizationEventsError as error: return Response({"detail": six.text_type(error)}, status=400) try: facets = discover.get_facets( query=request.GET.get("query"), params=params, referrer="api.organization-events-facets.top-tags", ) except discover.InvalidSearchQuery as error: raise ParseError(detail=six.text_type(error)) resp = defaultdict(lambda: {"key": "", "topValues": []}) for row in facets: values = resp[row.key] values["key"] = tagstore.get_standardized_key(row.key) values["topValues"].append({ "name": tagstore.get_tag_value_label(row.key, row.value), "value": row.value, "count": row.count, }) if "project" in resp: # Replace project ids with slugs as that is what we generally expose to users # and filter out projects that the user doesn't have access too. projects = { p.id: p.slug for p in self.get_projects(request, organization) } filtered_values = [] for v in resp["project"]["topValues"]: if v["value"] in projects: name = projects[v["value"]] v.update({"name": name}) filtered_values.append(v) resp["project"]["topValues"] = filtered_values return Response(resp.values())
def serialize(self, obj, attrs, user): from sentry import tagstore return { 'key': tagstore.get_standardized_key(obj.key), 'name': tagstore.get_tag_value_label(obj.key, obj.value), 'value': obj.value, 'count': obj.times_seen, 'lastSeen': obj.last_seen, 'firstSeen': obj.first_seen, }
def serialize(self, obj, attrs, user): from sentry import tagstore return { 'id': six.text_type(obj.id), 'name': attrs['name'], 'key': tagstore.get_standardized_key(obj.key), 'value': obj.value, 'count': obj.times_seen, 'lastSeen': obj.last_seen, 'firstSeen': obj.first_seen, }
def serialize(self, obj, attrs, user): from sentry import tagstore return { "id": six.text_type(obj.id), "name": attrs["name"], "key": tagstore.get_standardized_key(obj.key), "value": obj.value, "count": obj.times_seen, "lastSeen": obj.last_seen, "firstSeen": obj.first_seen, }
def get_attrs(self, item_list, user): from sentry import tagstore result = {} for item in item_list: key = tagstore.get_standardized_key(item.key) result[item] = { 'name': tagstore.get_tag_key_label(item.key), 'key': key, } return result
def serialize(self, obj, attrs, user): from sentry import tagstore output = { 'key': tagstore.get_standardized_key(obj.key), 'name': tagstore.get_tag_key_label(obj.key), 'uniqueValues': obj.values_seen, } if obj.count is not None: output['totalValues'] = obj.count if obj.top_values is not None: output['topValues'] = serialize(obj.top_values, user) return output
def get(self, request, project): tag_keys = tagstore.get_tag_keys(project.id) data = [] for tag_key in tag_keys: data.append({ 'id': six.text_type(tag_key.id), 'key': tagstore.get_standardized_key(tag_key.key), 'name': tag_key.get_label(), 'uniqueValues': tag_key.values_seen, }) return Response(data)
def passes(self, event, state): latest_release = self.get_latest_release(event) if not latest_release: return False releases = (v.lower() for k, v in event.tags if k.lower() == "release" or tagstore.get_standardized_key(k) == "release") for release in releases: if release == latest_release.version: return True return False
def passes(self, event, state, **kwargs): key = self.get_option('key') match = self.get_option('match') value = self.get_option('value') if not (key and match and value): return False value = value.lower() key = key.lower() tags = ( v.lower() for k, v in event.get_tags() if k.lower() == key or tagstore.get_standardized_key(k) == key ) if match == MatchType.EQUAL: for t_value in tags: if t_value == value: return True return False elif match == MatchType.NOT_EQUAL: for t_value in tags: if t_value == value: return False return True elif match == MatchType.STARTS_WITH: for t_value in tags: if t_value.startswith(value): return True return False elif match == MatchType.ENDS_WITH: for t_value in tags: if t_value.endswith(value): return True return False elif match == MatchType.CONTAINS: for t_value in tags: if value in t_value: return True return False elif match == MatchType.NOT_CONTAINS: for t_value in tags: if value in t_value: return False return True
def get(self, request, organization): if not self.has_feature(organization, request): return Response(status=404) try: params = self.get_snuba_params(request, organization) except NoProjects: return Response([]) with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"): with self.handle_query_errors(): facets = discover.get_facets( query=request.GET.get("query"), params=params, referrer="api.organization-events-facets.top-tags", ) with sentry_sdk.start_span(op="discover.endpoint", description="populate_results") as span: span.set_data("facet_count", len(facets or [])) resp = defaultdict(lambda: {"key": "", "topValues": []}) for row in facets: values = resp[row.key] values["key"] = tagstore.get_standardized_key(row.key) values["topValues"].append({ "name": tagstore.get_tag_value_label(row.key, row.value), "value": row.value, "count": row.count, }) if "project" in resp: # Replace project ids with slugs as that is what we generally expose to users # and filter out projects that the user doesn't have access too. projects = { p.id: p.slug for p in self.get_projects(request, organization) } filtered_values = [] for v in resp["project"]["topValues"]: if v["value"] in projects: name = projects[v["value"]] v.update({"name": name}) filtered_values.append(v) resp["project"]["topValues"] = filtered_values return Response(list(resp.values()))
def get_event_payload(self, event): props = { 'event_id': event.event_id, 'project_id': event.project.slug, 'transaction': event.get_tag('transaction') or '', 'release': event.get_tag('sentry:release') or '', 'environment': event.get_tag('environment') or '', 'type': event.get_event_type(), } props['tags'] = [[k.format(tagstore.get_standardized_key(k)), v] for k, v in event.get_tags()] for key, value in six.iteritems(event.interfaces): if key == 'request': headers = value.headers if not isinstance(headers, dict): headers = dict(headers or ()) props.update({ 'request_url': value.url, 'request_method': value.method, 'request_referer': headers.get('Referer', ''), }) elif key == 'exception': exc = value.values[0] props.update({ 'exception_type': exc.type, 'exception_value': exc.value, }) elif key == 'logentry': props.update({ 'message': value.formatted or value.message, }) elif key in ('csp', 'expectct', 'expectstable', 'hpkp'): props.update({ '{}_{}'.format(key.rsplit('.', 1)[-1].lower(), k): v for k, v in six.iteritems(value.to_json()) }) elif key == 'user': user_payload = {} if value.id: user_payload['user_id'] = value.id if value.email: user_payload['user_email_hash'] = md5_text( value.email).hexdigest() if value.ip_address: user_payload['user_ip_trunc'] = anonymize_ip( value.ip_address) if user_payload: props.update(user_payload) return props
def serialize(self, obj, attrs, user): from sentry import tagstore output = { "key": tagstore.get_standardized_key(obj.key), "name": tagstore.get_tag_key_label(obj.key), } if obj.values_seen is not None: output["uniqueValues"] = obj.values_seen if obj.count is not None: output["totalValues"] = obj.count if obj.top_values is not None: output["topValues"] = serialize(obj.top_values, user) return output
def passes(self, event, state, **kwargs): key = self.get_option('key') match = self.get_option('match') value = self.get_option('value') if not (key and match and value): return False value = value.lower() key = key.lower() tags = (v.lower() for k, v in event.get_tags() if k.lower() == key or tagstore.get_standardized_key(k) == key) if match == MatchType.EQUAL: for t_value in tags: if t_value == value: return True return False elif match == MatchType.NOT_EQUAL: for t_value in tags: if t_value == value: return False return True elif match == MatchType.STARTS_WITH: for t_value in tags: if t_value.startswith(value): return True return False elif match == MatchType.ENDS_WITH: for t_value in tags: if t_value.endswith(value): return True return False elif match == MatchType.CONTAINS: for t_value in tags: if value in t_value: return True return False elif match == MatchType.NOT_CONTAINS: for t_value in tags: if value in t_value: return False return True
def get_event_payload_properties(self, event): props = { "event_id": event.event_id, "issue_id": event.group_id, "project_id": event.project.slug, "transaction": event.get_tag("transaction") or "", "release": event.get_tag("sentry:release") or "", "environment": event.get_tag("environment") or "", "type": event.get_event_type(), } props["tags"] = [[k.format(tagstore.get_standardized_key(k)), v] for k, v in event.tags] for key, value in six.iteritems(event.interfaces): if key == "request": headers = value.headers if not isinstance(headers, dict): headers = dict(headers or ()) props.update({ "request_url": value.url, "request_method": value.method, "request_referer": headers.get("Referer", ""), }) elif key == "exception": exc = value.values[0] props.update({ "exception_type": exc.type, "exception_value": exc.value }) elif key == "logentry": props.update({"message": value.formatted or value.message}) elif key in ("csp", "expectct", "expectstable", "hpkp"): props.update({ u"{}_{}".format(key.rsplit(".", 1)[-1].lower(), k): v for k, v in six.iteritems(value.to_json()) }) elif key == "user": user_payload = {} if value.id: user_payload["user_id"] = value.id if value.email: user_payload["user_email_hash"] = md5_text( value.email).hexdigest() if value.ip_address: user_payload["user_ip_trunc"] = anonymize_ip( value.ip_address) if user_payload: props.update(user_payload) return props
def get(self, request, project): tag_keys = sorted(tagstore.get_tag_keys(project.id), key=lambda x: x.key) data = [] for tag_key in tag_keys: data.append( { 'id': six.text_type(tag_key.id), 'key': tagstore.get_standardized_key(tag_key.key), 'name': tagstore.get_tag_key_label(tag_key.key), 'uniqueValues': tag_key.values_seen, } ) return Response(data)
def get_event_payload(self, event): props = { 'event_id': event.event_id, 'project_id': event.project.slug, 'transaction': event.get_tag('transaction') or '', 'release': event.get_tag('sentry:release') or '', 'environment': event.get_tag('environment') or '', 'type': event.get_event_type(), } props['tags'] = [[k.format(tagstore.get_standardized_key(k)), v] for k, v in event.get_tags()] for key, value in six.iteritems(event.interfaces): if key == 'request': headers = value.headers if not isinstance(headers, dict): headers = dict(headers or ()) props.update({ 'request_url': value.url, 'request_method': value.method, 'request_referer': headers.get('Referer', ''), }) elif key == 'exception': exc = value.values[0] props.update({ 'exception_type': exc.type, 'exception_value': exc.value, }) elif key == 'logentry': props.update({ 'message': value.formatted or value.message, }) elif key in ('csp', 'expectct', 'expectstable', 'hpkp'): props.update({ '{}_{}'.format(key.rsplit('.', 1)[-1].lower(), k): v for k, v in six.iteritems(value.to_json()) }) elif key == 'user': user_payload = {} if value.id: user_payload['user_id'] = value.id if value.email: user_payload['user_email_hash'] = md5_text(value.email).hexdigest() if value.ip_address: user_payload['user_ip_trunc'] = anonymize_ip(value.ip_address) if user_payload: props.update(user_payload) return props
def build_tag_fields(event_for_tags, tags: Mapping[str, str] = None): fields = [] if tags: event_tags = event_for_tags.tags if event_for_tags else [] for key, value in event_tags: std_key = tagstore.get_standardized_key(key) if std_key not in tags: continue labeled_value = tagstore.get_tag_value_label(key, value) fields.append({ "title": std_key.encode("utf-8"), "value": labeled_value.encode("utf-8"), "short": True, }) return fields
def serialize(self, obj, attrs, user): from sentry import tagstore key = tagstore.get_standardized_key(obj.key) serialized = { 'key': key, 'name': tagstore.get_tag_value_label(obj.key, obj.value), 'value': obj.value, 'count': obj.times_seen, 'lastSeen': obj.last_seen, 'firstSeen': obj.first_seen, } query = convert_user_tag_to_query(key, obj.value) if query: serialized['query'] = query return serialized
def serialize(self, obj, attrs, user): from sentry import tagstore key = tagstore.get_standardized_key(obj.key) serialized = { "key": key, "name": tagstore.get_tag_value_label(obj.key, obj.value), "value": obj.value, "count": obj.times_seen, "lastSeen": obj.last_seen, "firstSeen": obj.first_seen, } query = convert_user_tag_to_query(key, obj.value) if query: serialized["query"] = query return serialized
def build_tag_fields( event_for_tags: Any, tags: Optional[Set[str]] = None ) -> Sequence[Mapping[str, Union[str, bool]]]: fields = [] if tags: event_tags = event_for_tags.tags if event_for_tags else [] for key, value in event_tags: std_key = tagstore.get_standardized_key(key) if std_key not in tags: continue labeled_value = tagstore.get_tag_value_label(key, value) fields.append({ "title": std_key.encode("utf-8"), "value": labeled_value.encode("utf-8"), "short": True, }) return fields
def get(self, request, organization): if not self.has_feature(organization, request): return Response(status=404) try: params = self.get_snuba_params(request, organization) except NoProjects: return Response([]) aggregate_column = request.GET.get("aggregateColumn", "duration") orderby = request.GET.getlist("order", None) with sentry_sdk.start_span(op="discover.endpoint", description="discover_query"): with self.handle_query_errors(): facets = discover.get_performance_facets( query=request.GET.get("query"), params=params, referrer= "api.organization-events-facets-performance.top-tags", aggregate_column=aggregate_column, orderby=orderby, ) with sentry_sdk.start_span(op="discover.endpoint", description="populate_results") as span: span.set_data("facet_count", len(facets or [])) resp = defaultdict(lambda: {"key": "", "topValues": []}) for row in facets: values = resp[row.key] values["key"] = tagstore.get_standardized_key(row.key) values["topValues"].append({ "name": tagstore.get_tag_value_label(row.key, row.value), "value": row.value, "count": row.count, "aggregate": row.performance, }) return Response(list(resp.values()))
def get_attrs(self, item_list, user): tag_labels = { t.key: t.get_label() for t in tagstore.get_tag_keys(item_list[0].project_id, [i.key for i in item_list]) } result = {} for item in item_list: key = tagstore.get_standardized_key(item.key) try: label = tag_labels[item.key] except KeyError: label = key result[item] = { 'name': label, 'key': key, } return result
def build_attachment(group, event=None, tags=None, identity=None, actions=None, rules=None): # XXX(dcramer): options are limited to 100 choices, even when nested status = group.get_status() members = get_member_assignees(group) teams = get_team_assignees(group) logo_url = absolute_uri(get_asset_url('sentry', 'images/sentry-email-avatar.png')) color = LEVEL_TO_COLOR.get( event.get_tag('level'), 'error') if event else LEVEL_TO_COLOR['error'] text = build_attachment_text(group, event) or '' if actions is None: actions = [] assignee = get_assignee(group) resolve_button = { 'name': 'resolve_dialog', 'value': 'resolve_dialog', 'type': 'button', 'text': 'Resolve...', } ignore_button = { 'name': 'status', 'value': 'ignored', 'type': 'button', 'text': 'Ignore', } has_releases = Release.objects.filter( projects=group.project, organization_id=group.project.organization_id ).exists() if not has_releases: resolve_button.update({ 'name': 'status', 'text': 'Resolve', 'value': 'resolved', }) if status == GroupStatus.RESOLVED: resolve_button.update({ 'name': 'status', 'text': 'Unresolve', 'value': 'unresolved', }) if status == GroupStatus.IGNORED: ignore_button.update({ 'text': 'Stop Ignoring', 'value': 'unresolved', }) option_groups = [] if teams: option_groups.append({ 'text': 'Teams', 'options': teams, }) if members: option_groups.append({ 'text': 'People', 'options': members, }) payload_actions = [ resolve_button, ignore_button, { 'name': 'assign', 'text': 'Select Assignee...', 'type': 'select', 'selected_options': [assignee], 'option_groups': option_groups, }, ] fields = [] if tags: event_tags = event.tags if event else group.get_latest_event().tags for key, value in event_tags: std_key = tagstore.get_standardized_key(key) if std_key not in tags: continue labeled_value = tagstore.get_tag_value_label(key, value) fields.append( { 'title': std_key.encode('utf-8'), 'value': labeled_value.encode('utf-8'), 'short': True, } ) if actions: action_texts = filter(None, [build_action_text(group, identity, a) for a in actions]) text += '\n' + '\n'.join(action_texts) color = ACTIONED_ISSUE_COLOR payload_actions = [] ts = group.last_seen if event: event_ts = event.datetime ts = max(ts, event_ts) footer = u'{}'.format(group.qualified_short_id) if rules: footer += u' via {}'.format(rules[0].label) if len(rules) > 1: footer += u' (+{} other)'.format(len(rules) - 1) return { 'fallback': u'[{}] {}'.format(group.project.slug, group.title), 'title': build_attachment_title(group, event), 'title_link': group.get_absolute_url(params={'referrer': 'slack'}), 'text': text, 'fields': fields, 'mrkdwn_in': ['text'], 'callback_id': json.dumps({'issue': group.id}), 'footer_icon': logo_url, 'footer': footer, 'ts': to_timestamp(ts), 'color': color, 'actions': payload_actions, }
def notify(self, notification): event = notification.event group = event.group project = group.project if not self.is_configured(project): return webhook = self.get_option('webhook', project) username = (self.get_option('username', project) or 'Sentry').strip() icon_url = self.get_option('icon_url', project) channel = (self.get_option('channel', project) or '').strip() title = event.title.encode('utf-8') # TODO(dcramer): we'd like this to be the event culprit, but Sentry # does not currently retain it if group.culprit: culprit = group.culprit.encode('utf-8') else: culprit = None project_name = project.get_full_name().encode('utf-8') fields = [] # They can be the same if there is no culprit # So we set culprit to an empty string instead of duplicating the text if not self.get_option('exclude_culprit', project) and culprit and title != culprit: fields.append({ 'title': 'Culprit', 'value': culprit, 'short': False, }) if not self.get_option('exclude_project', project): fields.append({ 'title': 'Project', 'value': project_name, 'short': True, }) if self.get_option('custom_message', project): fields.append({ 'title': 'Custom message', 'value': self.get_option('custom_message', project), 'short': False, }) if self.get_option('include_rules', project): rules = [] for rule in notification.rules: rule_link = '/%s/%s/settings/alerts/rules/%s/' % ( group.organization.slug, project.slug, rule.id) # Make sure it's an absolute uri since we're sending this # outside of Sentry into Slack rule_link = absolute_uri(rule_link) rules.append((rule_link, rule.label)) if rules: value = u', '.join(u'<{} | {}>'.format(*r) for r in rules) fields.append( { 'title': 'Triggered By', 'value': value.encode('utf-8'), 'short': False, } ) if self.get_option('include_tags', project): included_tags = set(self.get_tag_list('included_tag_keys', project) or []) excluded_tags = set(self.get_tag_list('excluded_tag_keys', project) or []) for tag_key, tag_value in self._get_tags(event): key = tag_key.lower() std_key = tagstore.get_standardized_key(key) if included_tags and key not in included_tags and std_key not in included_tags: continue if excluded_tags and (key in excluded_tags or std_key in excluded_tags): continue fields.append( { 'title': tag_key.encode('utf-8'), 'value': tag_value.encode('utf-8'), 'short': True, } ) payload = { 'attachments': [ { 'fallback': '[%s] %s' % (project_name, title), 'title': title, 'title_link': group.get_absolute_url(params={'referrer': 'slack'}), 'color': self.color_for_event(event), 'fields': fields, } ] } if username: payload['username'] = username.encode('utf-8') if channel: payload['channel'] = channel if icon_url: payload['icon_url'] = icon_url values = {'payload': json.dumps(payload)} # Apparently we've stored some bad data from before we used `URLField`. webhook = webhook.strip(' ') return http.safe_urlopen(webhook, method='POST', data=values)