def test_escaped_wildcard(self): assert get_snuba_query_args( 'release:3.1.\\* user.email:\\*@example.com') == { 'conditions': [ [[ 'match', ['tags[sentry:release]', "'(?i)^3\\.1\\.\\*$'"] ], '=', 1], [['match', ['email', "'(?i)^\*\\@example\\.com$'"]], '=', 1], ], 'filter_keys': {}, } assert get_snuba_query_args('release:\\\\\\*') == { 'conditions': [ [['match', ['tags[sentry:release]', "'(?i)^\\\\\\*$'"]], '=', 1], ], 'filter_keys': {}, } assert get_snuba_query_args('release:\\\\*') == { 'conditions': [ [['match', ['tags[sentry:release]', "'(?i)^\\\\.*$'"]], '=', 1], ], 'filter_keys': {}, }
def test_boolean_term_simple(self): assert get_snuba_query_args('user.email:[email protected] AND user.email:[email protected]') == { 'conditions': [ ['and', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]] ], 'filter_keys': {}, 'has_boolean_terms': True, } assert get_snuba_query_args('user.email:[email protected] OR user.email:[email protected]') == { 'conditions': [ ['or', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]] ], 'filter_keys': {}, 'has_boolean_terms': True, } assert get_snuba_query_args( 'user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] OR user.email:[email protected] AND user.email:[email protected] AND user.email:[email protected]' ) == { 'conditions': [ ['or', [ ['and', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]], ['or', [ ['and', [ ['email', '=', '*****@*****.**'], ['and', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]] ]], ['or', [ ['and', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]], ['and', [ ['email', '=', '*****@*****.**'], ['and', [ ['email', '=', '*****@*****.**'], ['email', '=', '*****@*****.**'] ]] ]] ]] ]] ]] ], 'filter_keys': {}, 'has_boolean_terms': True, }
def get_snuba_query_args(self, request, organization): params = self.get_filter_params(request, organization) group_ids = set(map(int, request.GET.getlist('group'))) if group_ids: projects = Project.objects.filter( organization=organization, group__id__in=group_ids, ).distinct() if any(p for p in projects if not request.access.has_project_access(p)): raise PermissionDenied params['issue.id'] = list(group_ids) params['project_id'] = list(set([p.id for p in projects] + params['project_id'])) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has( 'organizations:boolean-search', organization, actor=request.user ) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.') return snuba_args
def get_snuba_query_args_v2(self, request, organization): params = self.get_filter_params(request, organization) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) fields = request.GET.getlist('fields') if fields: snuba_args['selected_columns'] = fields else: raise OrganizationEventsError('No fields requested.') groupby = request.GET.getlist('groupby') if groupby: snuba_args['groupby'] = groupby orderby = request.GET.get('orderby') if orderby: snuba_args['orderby'] = orderby # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has('organizations:boolean-search', organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) return snuba_args
def create_snuba_subscription(project, dataset, query, aggregations, time_window, resolution): """ Creates a subscription to a snuba query. :param project: The project we're applying the query to :param dataset: The snuba dataset to query and aggregate over :param query: An event search query that we can parse and convert into a set of Snuba conditions :param aggregations: A list of aggregations to calculate over the time window :param time_window: The time window to aggregate over :param resolution: How often to receive updates/bucket size :return: A uuid representing the subscription id. """ # TODO: Might make sense to move this into snuba if we have wider use for # it. resp = safe_urlopen( settings.SENTRY_SNUBA + "/subscriptions", "POST", json={ "project_id": project.id, "dataset": dataset.value, # We only care about conditions here. Filter keys only matter for # filtering to project and groups. Projects are handled with an # explicit param, and groups can't be queried here. "conditions": get_snuba_query_args(query)["conditions"], "aggregates": [alert_aggregation_to_snuba[agg] for agg in aggregations], "time_window": time_window, "resolution": resolution, }, ) resp.raise_for_status() return uuid.UUID(resp.json()["subscription_id"])
def get_snuba_query_args(self, request, organization): params = self.get_filter_params(request, organization) try: return get_snuba_query_args(query=request.GET.get('query'), params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message)
def get_snuba_query_args(self, request, organization, params): query = request.GET.get('query') group_ids = request.GET.getlist('group') if group_ids: # TODO(mark) This parameter should be removed in the long term. # Instead of using this parameter clients should use `issue.id` # in their query string. try: group_ids = set(map(int, filter(None, group_ids))) except ValueError: raise OrganizationEventsError('Invalid group parameter. Values must be numbers') projects = Project.objects.filter( organization=organization, group__id__in=group_ids, ).distinct() if any(p for p in projects if not request.access.has_project_access(p)): raise PermissionDenied params['issue.id'] = list(group_ids) params['project_id'] = list(set([p.id for p in projects] + params['project_id'])) try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) sort = request.GET.getlist('sort') if sort: snuba_args['orderby'] = sort # Deprecated. `sort` should be used as it is supported by # more endpoints. orderby = request.GET.getlist('orderby') if orderby and 'orderby' not in snuba_args: snuba_args['orderby'] = orderby if request.GET.get('rollup'): try: snuba_args['rollup'] = int(request.GET.get('rollup')) except ValueError: raise OrganizationEventsError('rollup must be an integer.') fields = request.GET.getlist('field')[:] if fields: try: snuba_args.update(resolve_field_list(fields, snuba_args)) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has( 'organizations:boolean-search', organization, actor=request.user ) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.') return snuba_args
def bulk_build_incident_query_params(incidents, start=None, end=None): incident_groups = defaultdict(list) for incident_id, group_id in IncidentGroup.objects.filter( incident__in=incidents).values_list("incident_id", "group_id"): incident_groups[incident_id].append(group_id) incident_projects = defaultdict(list) for incident_id, project_id in IncidentProject.objects.filter( incident__in=incidents).values_list("incident_id", "project_id"): incident_projects[incident_id].append(project_id) query_args_list = [] for incident in incidents: params = { "start": incident.date_started if start is None else start, "end": incident.current_end_date if end is None else end, } group_ids = incident_groups[incident.id] if group_ids: params["issue.id"] = group_ids project_ids = incident_projects[incident.id] if project_ids: params["project_id"] = project_ids query_args_list.append(get_snuba_query_args(incident.query, params)) return query_args_list
def test_negation(self): assert get_snuba_query_args('!user.email:[email protected]') == { 'conditions': [ [['ifNull', ['email', "''"]], '!=', '*****@*****.**'], ], 'filter_keys': {}, }
def test_no_search(self): assert get_snuba_query_args( params={ 'project_id': [1, 2, 3], 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }) == { 'conditions': [], 'filter_keys': { 'project_id': [1, 2, 3] }, 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }
def get_snuba_query_args(self, request, organization): params = self.get_filter_params(request, organization) group_ids = set(map(int, request.GET.getlist('group'))) if group_ids: projects = Project.objects.filter( organization=organization, group__id__in=group_ids, ).distinct() if any(p for p in projects if not request.access.has_project_access(p)): raise PermissionDenied params['issue.id'] = list(group_ids) params['project_id'] = list( set([p.id for p in projects] + params['project_id'])) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has('organizations:boolean-search', organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) return snuba_args
def test_negation_get_snuba_query_args(self): assert get_snuba_query_args('!user.email:[email protected]') == { 'conditions': [ [['ifNull', ['email', "''"]], '!=', '*****@*****.**'], ], 'filter_keys': {}, }
def test_get_snuba_query_args(self): assert get_snuba_query_args('user.email:[email protected] release:1.2.1 hello') == { 'conditions': [ ['email', '=', '*****@*****.**'], ['sentry:release', '=', '1.2.1'], [['positionCaseInsensitive', ['message', "'hello'"]], '!=', 0], ] }
def test_wildcard(self): assert get_snuba_query_args('release:3.1.* user.email:*@example.com') == { 'conditions': [ [['match', ['tags[sentry:release]', "'(?i)^3\\.1\\..*$'"]], '=', 1], [['match', ['email', "'(?i)^.*\\@example\\.com$'"]], '=', 1], ], 'filter_keys': {}, }
def test_get_snuba_query_args_negated_wildcard(self): assert get_snuba_query_args('!release:3.1.* user.email:*@example.com') == { 'conditions': [ [['match', [['ifnull', ['tags[sentry:release]', "''"]], "'^3\\.1\\..*$'"]], '!=', 1], [['match', ['email', "'^.*\\@example\\.com$'"]], '=', 1], ], 'filter_keys': {}, }
def test_message_negative(self): assert get_snuba_query_args('!message:"post_process.process_error HTTPError 403"') == { 'filter_keys': {}, 'conditions': [[ ['positionCaseInsensitive', ['message', "'post_process.process_error HTTPError 403'"]], '=', 0, ]] }
def test_no_search(self): assert get_snuba_query_args(params={ 'project_id': [1, 2, 3], 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }) == { 'conditions': [], 'filter_keys': {'project_id': [1, 2, 3]}, 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }
def get_snuba_query_args_v2(self, request, organization, params): query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) fields = request.GET.getlist('field')[:] aggregations = [] groupby = request.GET.getlist('groupby') special_fields = set() if fields: # If project.name is requested, get the project.id from Snuba so we # can use this to look up the name in Sentry if 'project.name' in fields: fields.remove('project.name') if 'project.id' not in fields: fields.append('project.id') for field in fields[:]: if field in SPECIAL_FIELDS: special_fields.add(field) special_field = deepcopy(SPECIAL_FIELDS[field]) fields.remove(field) fields.extend(special_field.get('fields', [])) aggregations.extend(special_field.get('aggregations', [])) groupby.extend(special_field.get('groupby', [])) snuba_args['selected_columns'] = fields self._filter_unspecified_special_fields_in_conditions( snuba_args, special_fields) if aggregations: snuba_args['aggregations'] = aggregations if groupby: snuba_args['groupby'] = groupby orderby = request.GET.getlist('orderby') if orderby: snuba_args['orderby'] = orderby # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has('organizations:boolean-search', organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) return snuba_args
def test_project_name(self): p1 = self.create_project(organization=self.organization) p2 = self.create_project(organization=self.organization) params = { 'project_id': [p1.id, p2.id], } assert get_snuba_query_args('project.name:{}'.format(p1.slug), params) == { 'conditions': [['project_id', '=', p1.id]], 'filter_keys': { 'project_id': [p1.id, p2.id], } }
def test_issue_filter(self): assert get_snuba_query_args('issue.id:1') == { 'conditions': [], 'filter_keys': { 'issue': [1], }, } assert get_snuba_query_args('issue.id:1 issue.id:2 issue.id:3') == { 'conditions': [], 'filter_keys': { 'issue': [1, 2, 3], }, } assert get_snuba_query_args('issue.id:1 user.email:[email protected]') == { 'conditions': [ ['email', '=', '*****@*****.**'] ], 'filter_keys': { 'issue': [1], }, }
def _get_events_snuba(self, request, group, environments, query, tags, start, end): default_end = timezone.now() default_start = default_end - timedelta(days=90) params = { 'issue.id': [group.id], 'project_id': [group.project_id], 'start': start if start else default_start, 'end': end if end else default_end } direct_hit_resp = get_direct_hit_response(request, query, params, 'api.group-events') if direct_hit_resp: return direct_hit_resp if environments: params['environment'] = [env.name for env in environments] full = request.GET.get('full', False) snuba_args = get_snuba_query_args(request.GET.get('query', None), params) # TODO(lb): remove once boolean search is fully functional if snuba_args: has_boolean_op_flag = features.has('organizations:boolean-search', group.project.organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise GroupEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) snuba_cols = SnubaEvent.minimal_columns if full else SnubaEvent.selected_columns data_fn = partial( # extract 'data' from raw_query result lambda *args, **kwargs: raw_query(*args, **kwargs)['data'], selected_columns=snuba_cols, orderby='-timestamp', referrer='api.group-events', **snuba_args) serializer = EventSerializer() if full else SimpleEventSerializer() return self.paginate( request=request, on_results=lambda results: serialize( [SnubaEvent(row) for row in results], request.user, serializer), paginator=GenericOffsetPaginator(data_fn=data_fn))
def build_incident_query_params(incident): params = {'start': incident.date_started, 'end': incident.current_end_date} group_ids = list(IncidentGroup.objects.filter( incident=incident, ).values_list('group_id', flat=True)) if group_ids: params['issue.id'] = group_ids project_ids = list(IncidentProject.objects.filter( incident=incident, ).values_list('project_id', flat=True)) if project_ids: params['project_id'] = project_ids return get_snuba_query_args(incident.query, params)
def test_get_snuba_query_args(self): assert get_snuba_query_args('user.email:[email protected] release:1.2.1 hello', { 'project_id': [1, 2, 3], 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }) == { 'conditions': [ ['email', '=', '*****@*****.**'], ['sentry:release', '=', '1.2.1'], [['positionCaseInsensitive', ['message', "'hello'"]], '!=', 0], ], 'filter_keys': {'project_id': [1, 2, 3]}, 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }
def get(self, request, organization): # Check for a direct hit on event ID query = request.GET.get('query', '').strip() if is_event_id(query): try: snuba_args = get_snuba_query_args( query=u'id:{}'.format(query), params=self.get_filter_params(request, organization)) results = raw_query( selected_columns=SnubaEvent.selected_columns, referrer='api.organization-events', **snuba_args )['data'] if len(results) == 1: response = Response( serialize([SnubaEvent(row) for row in results], request.user) ) response['X-Sentry-Direct-Hit'] = '1' return response except (OrganizationEventsError, NoProjects): pass try: snuba_args = self.get_snuba_query_args(request, organization) except OrganizationEventsError as exc: return Response({'detail': exc.message}, status=400) except NoProjects: # return empty result if org doesn't have projects # or user doesn't have access to projects in org data_fn = lambda *args, **kwargs: [] else: data_fn = partial( # extract 'data' from raw_query result lambda *args, **kwargs: raw_query(*args, **kwargs)['data'], selected_columns=SnubaEvent.selected_columns, orderby='-timestamp', referrer='api.organization-events', **snuba_args ) return self.paginate( request=request, on_results=lambda results: serialize( [SnubaEvent(row) for row in results], request.user), paginator=GenericOffsetPaginator(data_fn=data_fn) )
def _get_events_snuba(self, request, group, environments, query, tags, start, end): default_end = timezone.now() default_start = default_end - timedelta(days=90) params = { 'issue.id': [group.id], 'project_id': [group.project_id], 'start': start if start else default_start, 'end': end if end else default_end } direct_hit_resp = get_direct_hit_response(request, query, params, 'api.group-events') if direct_hit_resp: return direct_hit_resp if environments: params['environment'] = [env.name for env in environments] full = request.GET.get('full', False) snuba_args = get_snuba_query_args(request.GET.get('query', None), params) # TODO(lb): remove once boolean search is fully functional if snuba_args: has_boolean_op_flag = features.has( 'organizations:boolean-search', group.project.organization, actor=request.user ) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise GroupEventsError( 'Boolean search operator OR and AND not allowed in this search.') snuba_cols = SnubaEvent.minimal_columns if full else SnubaEvent.selected_columns data_fn = partial( # extract 'data' from raw_query result lambda *args, **kwargs: raw_query(*args, **kwargs)['data'], selected_columns=snuba_cols, orderby='-timestamp', referrer='api.group-events', **snuba_args ) serializer = EventSerializer() if full else SimpleEventSerializer() return self.paginate( request=request, on_results=lambda results: serialize( [SnubaEvent(row) for row in results], request.user, serializer), paginator=GenericOffsetPaginator(data_fn=data_fn) )
def test_simple(self): assert get_snuba_query_args('user.email:[email protected] release:1.2.1 fruit:apple hello', { 'project_id': [1, 2, 3], 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }) == { 'conditions': [ ['email', '=', '*****@*****.**'], ['tags[sentry:release]', '=', '1.2.1'], [['ifNull', ['tags[fruit]', "''"]], '=', 'apple'], [['positionCaseInsensitive', ['message', "'hello'"]], '!=', 0], ], 'filter_keys': {'project_id': [1, 2, 3]}, 'start': datetime.datetime(2015, 5, 18, 10, 15, 1, tzinfo=timezone.utc), 'end': datetime.datetime(2015, 5, 19, 10, 15, 1, tzinfo=timezone.utc), }
def get_direct_hit_response(request, query, snuba_params, referrer): """ Checks whether a query is a direct hit for an event, and if so returns a response. Otherwise returns None """ event_id = normalize_event_id(query) if event_id: snuba_args = get_snuba_query_args(query=u"id:{}".format(event_id), params=snuba_params) results = eventstore.get_events(referrer=referrer, **snuba_args) if len(results) == 1: response = Response(serialize(results, request.user)) response["X-Sentry-Direct-Hit"] = "1" return response
def _get_events_snuba(self, request, group, environments, query, tags, start, end): default_end = timezone.now() default_start = default_end - timedelta(days=90) params = { "issue.id": [group.id], "project_id": [group.project_id], "start": start if start else default_start, "end": end if end else default_end, } direct_hit_resp = get_direct_hit_response(request, query, params, "api.group-events") if direct_hit_resp: return direct_hit_resp if environments: params["environment"] = [env.name for env in environments] full = request.GET.get("full", False) snuba_args = get_snuba_query_args(request.GET.get("query", None), params) # TODO(lb): remove once boolean search is fully functional if snuba_args: has_boolean_op_flag = features.has("organizations:boolean-search", group.project.organization, actor=request.user) if snuba_args.pop("has_boolean_terms", False) and not has_boolean_op_flag: raise GroupEventsError( "Boolean search operator OR and AND not allowed in this search." ) snuba_cols = None if full else eventstore.full_columns data_fn = partial(eventstore.get_events, additional_columns=snuba_cols, referrer="api.group-events", **snuba_args) serializer = EventSerializer() if full else SimpleEventSerializer() return self.paginate( request=request, on_results=lambda results: serialize(results, request.user, serializer), paginator=GenericOffsetPaginator(data_fn=data_fn), )
def get_snuba_query_args(self, request, organization, params): query = request.GET.get("query") try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) sort = request.GET.getlist("sort") if sort: snuba_args["orderby"] = sort # Deprecated. `sort` should be used as it is supported by # more endpoints. orderby = request.GET.getlist("orderby") if orderby and "orderby" not in snuba_args: snuba_args["orderby"] = orderby if request.GET.get("rollup"): try: snuba_args["rollup"] = int(request.GET.get("rollup")) except ValueError: raise OrganizationEventsError("rollup must be an integer.") fields = request.GET.getlist("field")[:] if fields: try: snuba_args.update(resolve_field_list(fields, snuba_args)) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) reference_event_id = request.GET.get("referenceEvent") if reference_event_id: reference_event = find_reference_event(snuba_args, reference_event_id) snuba_args["conditions"] = get_reference_event_conditions( snuba_args, reference_event.snuba_data) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has("organizations:boolean-search", organization, actor=request.user) if snuba_args.pop("has_boolean_terms", False) and not has_boolean_op_flag: raise OrganizationEventsError( "Boolean search operator OR and AND not allowed in this search." ) return snuba_args
def get_snuba_query_args(self, request, organization): params = self.get_filter_params(request, organization) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has('organizations:boolean-search', organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) return snuba_args
def get_direct_hit_response(request, query, snuba_params, referrer): """ Checks whether a query is a direct hit for an event, and if so returns a response. Otherwise returns None """ event_id = normalize_event_id(query) if event_id: snuba_args = get_snuba_query_args(query=u'id:{}'.format(event_id), params=snuba_params) results = raw_query(selected_columns=SnubaEvent.selected_columns, referrer=referrer, **snuba_args)['data'] if len(results) == 1: response = Response( serialize([SnubaEvent(row) for row in results], request.user)) response['X-Sentry-Direct-Hit'] = '1' return response
def get_snuba_query_args(self, request, organization): params = self.get_filter_params(request, organization) group_ids = request.GET.getlist('group') if group_ids: try: group_ids = set(map(int, filter(None, group_ids))) except ValueError: raise OrganizationEventsError('Invalid group parameter. Values must be numbers') projects = Project.objects.filter( organization=organization, group__id__in=group_ids, ).distinct() if any(p for p in projects if not request.access.has_project_access(p)): raise PermissionDenied params['issue.id'] = list(group_ids) params['project_id'] = list(set([p.id for p in projects] + params['project_id'])) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # Filter out special aggregates. conditions = [] for condition in snuba_args.get('conditions', []): field_name = condition[0] if isinstance(field_name, (list, tuple)) or field_name not in SPECIAL_FIELDS: conditions.append(condition) snuba_args['conditions'] = conditions # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has( 'organizations:boolean-search', organization, actor=request.user ) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.') return snuba_args
def get_snuba_query_args_v2(self, request, organization, params): query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) fields = request.GET.getlist('field')[:] if fields: # If project.name is requested, get the project.id from Snuba so we # can use this to look up the name in Sentry if 'project.name' in fields: fields.remove('project.name') if 'project.id' not in fields: fields.append('project.id') snuba_args['selected_columns'] = fields aggregations = request.GET.getlist('aggregation') if aggregations: snuba_args['aggregations'] = [ aggregation.split(',') for aggregation in aggregations ] groupby = request.GET.getlist('groupby') if groupby: snuba_args['groupby'] = groupby orderby = request.GET.get('orderby') if orderby: snuba_args['orderby'] = orderby # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has('organizations:boolean-search', organization, actor=request.user) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.' ) return snuba_args
def get_snuba_query_args_legacy(self, request, organization): params = self.get_filter_params(request, organization) group_ids = request.GET.getlist('group') if group_ids: # TODO(mark) This parameter should be removed in the long term. # Instead of using this parameter clients should use `issue.id` # in their query string. try: group_ids = set(map(int, filter(None, group_ids))) except ValueError: raise OrganizationEventsError('Invalid group parameter. Values must be numbers') projects = Project.objects.filter( organization=organization, group__id__in=group_ids, ).distinct() if any(p for p in projects if not request.access.has_project_access(p)): raise PermissionDenied params['issue.id'] = list(group_ids) params['project_id'] = list(set([p.id for p in projects] + params['project_id'])) query = request.GET.get('query') try: snuba_args = get_snuba_query_args(query=query, params=params) except InvalidSearchQuery as exc: raise OrganizationEventsError(exc.message) # Filter out special aggregates. self._filter_unspecified_special_fields_in_conditions(snuba_args, set()) # TODO(lb): remove once boolean search is fully functional has_boolean_op_flag = features.has( 'organizations:boolean-search', organization, actor=request.user ) if snuba_args.pop('has_boolean_terms', False) and not has_boolean_op_flag: raise OrganizationEventsError( 'Boolean search operator OR and AND not allowed in this search.') return snuba_args
def _create_in_snuba(project, dataset, query, aggregation, time_window, resolution): response = _snuba_pool.urlopen( "POST", "/subscriptions", body=json.dumps({ "project_id": project.id, "dataset": dataset.value, # We only care about conditions here. Filter keys only matter for # filtering to project and groups. Projects are handled with an # explicit param, and groups can't be queried here. "conditions": get_snuba_query_args(query)["conditions"], "aggregates": [query_aggregation_to_snuba[aggregation]], "time_window": time_window, "resolution": resolution, }), retries=False, ) if response.status != 202: raise SnubaError("HTTP %s response from Snuba!" % response.status) return json.loads(response.data)["subscription_id"]
def get_direct_hit_response(request, query, snuba_params, referrer): """ Checks whether a query is a direct hit for an event, and if so returns a response. Otherwise returns None """ if is_event_id(query): snuba_args = get_snuba_query_args( query=u'id:{}'.format(query), params=snuba_params) results = raw_query( selected_columns=SnubaEvent.selected_columns, referrer=referrer, **snuba_args )['data'] if len(results) == 1: response = Response( serialize([SnubaEvent(row) for row in results], request.user) ) response['X-Sentry-Direct-Hit'] = '1' return response
def get(self, request, organization): try: start, end = get_date_range_from_params(request.GET) except InvalidParams as exc: return Response({'detail': exc.message}, status=400) try: project_ids = self.get_project_ids(request, organization) except ValueError: return Response({'detail': 'Invalid project ids'}, status=400) environments = self.get_environments(request, organization) params = { 'start': start, 'end': end, 'project_id': project_ids, } if environments: params['environment'] = environments try: snuba_args = get_snuba_query_args(query=request.GET.get('query'), params=params) except InvalidSearchQuery as exc: return Response({'detail': exc.message}, status=400) data_fn = partial( # extract 'data' from raw_query result lambda *args, **kwargs: raw_query(*args, **kwargs)['data'], selected_columns=SnubaEvent.selected_columns, orderby='-timestamp', referrer='api.organization-events', **snuba_args) return self.paginate(request=request, on_results=lambda results: serialize( [SnubaEvent(row) for row in results], request.user), paginator=GenericOffsetPaginator(data_fn=data_fn))
def test_not_has(self): assert get_snuba_query_args('!has:release') == { 'filter_keys': {}, 'conditions': [[['isNull', ['tags[sentry:release]']], '=', 1]] }
def test_get_snuba_query_args_has(self): assert get_snuba_query_args('has:release') == { 'filter_keys': {}, 'conditions': [[['ifNull', ['tags[sentry:release]', "''"]], '!=', '']] }