|
@@ -12,12 +12,17 @@ from functools32 import partial
|
|
|
from sentry import options, quotas, tagstore
|
|
|
from sentry.api.base import DocSection, EnvironmentMixin
|
|
|
from sentry.api.bases import GroupEndpoint
|
|
|
+from sentry.api.exceptions import ResourceDoesNotExist
|
|
|
+from sentry.api.helpers.environments import get_environments
|
|
|
from sentry.api.serializers.models.event import SnubaEvent
|
|
|
from sentry.api.serializers import serialize
|
|
|
from sentry.api.paginator import DateTimePaginator, GenericOffsetPaginator
|
|
|
-from sentry.models import Environment, Event, Group
|
|
|
-from sentry.search.utils import parse_query
|
|
|
-from sentry.search.utils import InvalidQuery
|
|
|
+from sentry.api.utils import get_date_range_from_params
|
|
|
+from sentry.models import Event, Group
|
|
|
+from sentry.search.utils import (
|
|
|
+ InvalidQuery,
|
|
|
+ parse_query,
|
|
|
+)
|
|
|
from sentry.utils.apidocs import scenario, attach_scenarios
|
|
|
from sentry.utils.validators import is_event_id
|
|
|
from sentry.utils.snuba import raw_query
|
|
@@ -49,18 +54,26 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
- environment = self._get_environment(request, group)
|
|
|
- query, tags = self._get_search_query_and_tags(request, group, environment)
|
|
|
+ environments = get_environments(request, group.project.organization)
|
|
|
+ query, tags = self._get_search_query_and_tags(
|
|
|
+ request,
|
|
|
+ group,
|
|
|
+ environments,
|
|
|
+ )
|
|
|
except InvalidQuery as exc:
|
|
|
return Response({'detail': six.text_type(exc)}, status=400)
|
|
|
- except NoResults:
|
|
|
+ except (NoResults, ResourceDoesNotExist):
|
|
|
return Response([])
|
|
|
|
|
|
- use_snuba = options.get('snuba.events-queries.enabled')
|
|
|
+ use_snuba = (
|
|
|
+ request.GET.get('enable_snuba') == '1'
|
|
|
+ or options.get('snuba.events-queries.enabled')
|
|
|
+ )
|
|
|
backend = self._get_events_snuba if use_snuba else self._get_events_legacy
|
|
|
- return backend(request, group, environment, query, tags)
|
|
|
+ start, end = get_date_range_from_params(request.GET, optional=True)
|
|
|
+ return backend(request, group, environments, query, tags, start, end)
|
|
|
|
|
|
- def _get_events_snuba(self, request, group, environment, query, tags):
|
|
|
+ def _get_events_snuba(self, request, group, environments, query, tags, start, end):
|
|
|
conditions = []
|
|
|
if query:
|
|
|
msg_substr = ['positionCaseInsensitive', ['message', "'%s'" % (query,)]]
|
|
@@ -72,14 +85,18 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
conditions.append(message_condition)
|
|
|
|
|
|
if tags:
|
|
|
- conditions.extend([[u'tags[{}]'.format(k), '=', v] for (k, v) in tags.items()])
|
|
|
+ for tag_name, tag_val in tags.items():
|
|
|
+ operator = 'IN' if isinstance(tag_val, list) else '='
|
|
|
+ conditions.append([u'tags[{}]'.format(tag_name), operator, tag_val])
|
|
|
+
|
|
|
+ default_end = timezone.now()
|
|
|
+ default_start = default_end - timedelta(days=90)
|
|
|
|
|
|
- now = timezone.now()
|
|
|
data_fn = partial(
|
|
|
# extract 'data' from raw_query result
|
|
|
lambda *args, **kwargs: raw_query(*args, **kwargs)['data'],
|
|
|
- start=now - timedelta(days=90),
|
|
|
- end=now,
|
|
|
+ start=max(start, default_start) if start else default_start,
|
|
|
+ end=min(end, default_end) if end else default_end,
|
|
|
conditions=conditions,
|
|
|
filter_keys={
|
|
|
'project_id': [group.project_id],
|
|
@@ -97,7 +114,16 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
paginator=GenericOffsetPaginator(data_fn=data_fn)
|
|
|
)
|
|
|
|
|
|
- def _get_events_legacy(self, request, group, environment, query, tags):
|
|
|
+ def _get_events_legacy(
|
|
|
+ self,
|
|
|
+ request,
|
|
|
+ group,
|
|
|
+ environments,
|
|
|
+ query,
|
|
|
+ tags,
|
|
|
+ start,
|
|
|
+ end,
|
|
|
+ ):
|
|
|
events = Event.objects.filter(group_id=group.id)
|
|
|
|
|
|
if query:
|
|
@@ -112,8 +138,10 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
event_filter = tagstore.get_group_event_filter(
|
|
|
group.project_id,
|
|
|
group.id,
|
|
|
- environment.id if environment is not None else None,
|
|
|
+ [env.id for env in environments],
|
|
|
tags,
|
|
|
+ start,
|
|
|
+ end,
|
|
|
)
|
|
|
|
|
|
if not event_filter:
|
|
@@ -121,6 +149,12 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
|
|
|
events = events.filter(**event_filter)
|
|
|
|
|
|
+ # Filter start/end here in case we didn't filter by tags at all
|
|
|
+ if start:
|
|
|
+ events = events.filter(datetime__gte=start)
|
|
|
+ if end:
|
|
|
+ events = events.filter(datetime__lte=end)
|
|
|
+
|
|
|
# filter out events which are beyond the retention period
|
|
|
retention = quotas.get_event_retention(organization=group.project.organization)
|
|
|
if retention:
|
|
@@ -136,16 +170,7 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
paginator_cls=DateTimePaginator,
|
|
|
)
|
|
|
|
|
|
- def _get_environment(self, request, group):
|
|
|
- try:
|
|
|
- return self._get_environment_from_request(
|
|
|
- request,
|
|
|
- group.project.organization_id,
|
|
|
- )
|
|
|
- except Environment.DoesNotExist:
|
|
|
- raise NoResults
|
|
|
-
|
|
|
- def _get_search_query_and_tags(self, request, group, environment=None):
|
|
|
+ def _get_search_query_and_tags(self, request, group, environments=None):
|
|
|
raw_query = request.GET.get('query')
|
|
|
|
|
|
if raw_query:
|
|
@@ -156,15 +181,22 @@ class GroupEventsEndpoint(GroupEndpoint, EnvironmentMixin):
|
|
|
query = None
|
|
|
tags = {}
|
|
|
|
|
|
- if environment is not None:
|
|
|
- if 'environment' in tags and tags['environment'] != environment.name:
|
|
|
- # An event can only be associated with a single
|
|
|
- # environment, so if the environment associated with
|
|
|
- # the request is different than the environment
|
|
|
- # provided as a tag lookup, the query cannot contain
|
|
|
- # any valid results.
|
|
|
- raise NoResults
|
|
|
+ if environments:
|
|
|
+ env_names = set(env.name for env in environments)
|
|
|
+ if 'environment' in tags:
|
|
|
+ # If a single environment was passed as part of the query, then
|
|
|
+ # we'll just search for that individual environment in this
|
|
|
+ # query, even if more are selected.
|
|
|
+ if tags['environment'] not in env_names:
|
|
|
+ # An event can only be associated with a single
|
|
|
+ # environment, so if the environments associated with
|
|
|
+ # the request don't contain the environment provided as a
|
|
|
+ # tag lookup, the query cannot contain any valid results.
|
|
|
+ raise NoResults
|
|
|
else:
|
|
|
- tags['environment'] = environment.name
|
|
|
+ # XXX: Handle legacy backends here. Just store environment as a
|
|
|
+ # single tag if we only have one so that we don't break existing
|
|
|
+ # usage.
|
|
|
+ tags['environment'] = list(env_names) if len(env_names) > 1 else env_names.pop()
|
|
|
|
|
|
return query, tags
|