Exemplo n.º 1
0
    def get(self, request, organization):
        """
        List an Organization's Issues
        `````````````````````````````

        Return a list of issues (groups) bound to an organization.  All parameters are
        supplied as query string parameters.

        A default query of ``is:unresolved`` is applied. To return results
        with other statuses send an new query value (i.e. ``?query=`` for all
        results).

        The ``groupStatsPeriod`` parameter can be used to select the timeline
        stats which should be present. Possible values are: '' (disable),
        '24h', '14d'

        The ``statsPeriod`` parameter can be used to select a date window starting
        from now. Ex. ``14d``.

        The ``start`` and ``end`` parameters can be used to select an absolute
        date period to fetch issues from.

        :qparam string statsPeriod: an optional stat period (can be one of
                                    ``"24h"``, ``"14d"``, and ``""``).
        :qparam string groupStatsPeriod: an optional stat period (can be one of
                                    ``"24h"``, ``"14d"``, and ``""``).
        :qparam string start:       Beginning date. You must also provide ``end``.
        :qparam string end:         End date. You must also provide ``start``.
        :qparam bool shortIdLookup: if this is set to true then short IDs are
                                    looked up by this function as well.  This
                                    can cause the return value of the function
                                    to return an event issue of a different
                                    project which is why this is an opt-in.
                                    Set to `1` to enable.
        :qparam querystring query: an optional Sentry structured search
                                   query.  If not provided an implied
                                   ``"is:unresolved"`` is assumed.)
        :pparam string organization_slug: the slug of the organization the
                                          issues belong to.
        :auth: required
        TODO(Chris F.): Add details on expand/collapse.
        """
        stats_period = request.GET.get("groupStatsPeriod")
        try:
            start, end = get_date_range_from_params(request.GET)
        except InvalidParams as e:
            raise ParseError(detail=six.text_type(e))

        expand = request.GET.getlist("expand", [])
        collapse = request.GET.getlist("collapse", [])
        has_inbox = features.has("organizations:inbox", organization, actor=request.user)

        if stats_period not in (None, "", "24h", "14d", "auto"):
            return Response({"detail": ERR_INVALID_STATS_PERIOD}, status=400)
        elif stats_period is None:
            # default
            stats_period = "24h"
        elif stats_period == "":
            # disable stats
            stats_period = None

        if stats_period == "auto":
            stats_period_start = start
            stats_period_end = end
        else:
            stats_period_start = None
            stats_period_end = None

        environments = self.get_environments(request, organization)

        serializer = functools.partial(
            StreamGroupSerializerSnuba,
            environment_ids=[env.id for env in environments],
            stats_period=stats_period,
            stats_period_start=stats_period_start,
            stats_period_end=stats_period_end,
            expand=expand,
            collapse=collapse,
            has_inbox=has_inbox,
        )

        projects = self.get_projects(request, organization)
        project_ids = [p.id for p in projects]

        if not projects:
            return Response([])

        if len(projects) > 1 and not features.has(
            "organizations:global-views", organization, actor=request.user
        ):
            return Response(
                {"detail": "You do not have the multi project stream feature enabled"}, status=400
            )

        # we ignore date range for both short id and event ids
        query = request.GET.get("query", "").strip()
        if query:
            # check to see if we've got an event ID
            event_id = normalize_event_id(query)
            if event_id:
                # For a direct hit lookup we want to use any passed project ids
                # (we've already checked permissions on these) plus any other
                # projects that the user is a member of. This gives us a better
                # chance of returning the correct result, even if the wrong
                # project is selected.
                direct_hit_projects = set(project_ids) | set(
                    [project.id for project in request.access.projects]
                )
                groups = list(Group.objects.filter_by_event_id(direct_hit_projects, event_id))
                if len(groups) == 1:
                    response = Response(
                        serialize(groups, request.user, serializer(matching_event_id=event_id))
                    )
                    response["X-Sentry-Direct-Hit"] = "1"
                    return response

                if groups:
                    return Response(serialize(groups, request.user, serializer()))

            group = get_by_short_id(organization.id, request.GET.get("shortIdLookup"), query)
            if group is not None:
                # check all projects user has access to
                if request.access.has_project_access(group.project):
                    response = Response(serialize([group], request.user, serializer()))
                    response["X-Sentry-Direct-Hit"] = "1"
                    return response

        # If group ids specified, just ignore any query components
        try:
            group_ids = set(map(int, request.GET.getlist("group")))
        except ValueError:
            return Response({"detail": "Group ids must be integers"}, status=400)

        if group_ids:
            groups = list(Group.objects.filter(id__in=group_ids, project_id__in=project_ids))
            if any(g for g in groups if not request.access.has_project_access(g.project)):
                raise PermissionDenied
            return Response(serialize(groups, request.user, serializer()))

        try:
            cursor_result, query_kwargs = self._search(
                request,
                organization,
                projects,
                environments,
                {"count_hits": True, "date_to": end, "date_from": start},
            )
        except (ValidationError, discover.InvalidSearchQuery) as exc:
            return Response({"detail": six.text_type(exc)}, status=400)

        results = list(cursor_result)

        context = serialize(
            results,
            request.user,
            serializer(
                start=start,
                end=end,
                search_filters=query_kwargs["search_filters"]
                if "search_filters" in query_kwargs
                else None,
            ),
        )

        # HACK: remove auto resolved entries
        # TODO: We should try to integrate this into the search backend, since
        # this can cause us to arbitrarily return fewer results than requested.
        status = [
            search_filter
            for search_filter in query_kwargs.get("search_filters", [])
            if search_filter.key.name == "status"
        ]
        if status and status[0].value.raw_value == GroupStatus.UNRESOLVED:
            context = [r for r in context if r["status"] == "unresolved"]

        response = Response(context)

        self.add_cursor_headers(request, response, cursor_result)

        # TODO(jess): add metrics that are similar to project endpoint here
        return response
Exemplo n.º 2
0
 def get(self, request, pk, format=None):
     if not PERMISSIONS.can_edit(request.user, Page.objects.get(page_content_items__id=pk)):
         return _403_FORBIDDEN_RESPONSE
     return Response('Exposes the `PageContentItem.move` method')
Exemplo n.º 3
0
 def get(self, request, format=None):
     """
     Provide jqTree data for the PageSelect dialog.
     """
     return Response(Page.objects.create_jqtree_data(request.user))
Exemplo n.º 4
0
 def get(self, request, format=None):
     """
     Order a scooter within a certain radius.
     """
     self.order_scooter(0.3)
     return Response(["Scooter view"])
Exemplo n.º 5
0
 def post(self, request):
     ps = request.data.get("row")
     if re.search('[a-zA-Z]', ps):
         ps = 10
     request.session['page_size'] = ps
     return Response(status=status.HTTP_200_OK)
Exemplo n.º 6
0
 def get(self, request, pk, format=None):
     user = self.get_object(pk)
     return Response(user.to_json())
Exemplo n.º 7
0
 def delete(self, request, pk, format=None):
     user = self.get_object(pk)
     user.delete()
     return Response(status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 8
0
    def delete(self, request):
        token = get_object_or_404(Token, user=request.user)

        token.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 9
0
def getTopProducts(request):
    products = Product.objects.filter(rating__gte=4).order_by('-rating')[0:5]
    serializer = ProductSerializer(products, many=True)
    return Response(serializer.data)
Exemplo n.º 10
0
 def get_paginated_response(self, data):
     return Response({
         'count': len(data),
         'results': data
     })
Exemplo n.º 11
0
    def retrieve(self, request, pk=None):
        zone = self.get_object_or_404(pk)
        serializer = ZoneSerializer(zone)

        return Response(serializer.data)
Exemplo n.º 12
0
    def destroy(self, request, pk=None):
        integration = self.get_object_or_404(pk)
        integration.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)
Exemplo n.º 13
0
    def retrieve(self, request, pk=None):
        integration = self.get_object_or_404(pk)
        serializer = self.get_serializer(integration)

        return Response(serializer.data)
Exemplo n.º 14
0
 def retrieve(self, request, pk=None):
     template = self.get_object_or_404(pk)
     serializer = TemplateSerializer(template)
     return Response(serializer.data)
Exemplo n.º 15
0
 def get(self, request, format=None):
     users = SomeUser.objects.all()
     return Response(users)
Exemplo n.º 16
0
def productView(request):
    pk = request.query_params.get('pk')
    type = request.query_params.get('type')
    query = request.query_params.get('keyword')
    page = request.query_params.get('page')
    user = request.user
    data = request.data

    if type == "getproducts":
        if query == None:
            query = ""
        products = Product.objects.filter(name__icontains=query)
        # here the number of products per page
        paginator = Paginator(products, 5)
        if page == None:
            page = 1
        try:
            products = paginator.page(page)
        except EmptyPage:
            products = paginator.page(paginator.num_pages)
        page = int(page)
        serializer = ProductSerializer(products, many=True)
        return Response({'products': serializer.data, 'page': page, 'pages': paginator.num_pages})

    elif type == "getproduct":
        product = Product.objects.get(_id=pk)
        serializer = ProductSerializer(product, many=False)
        return Response(serializer.data)

    elif type == "gettop":
        products = Product.objects.filter(
            rating__gte=4).order_by('-rating')[0:5]
        serializer = ProductSerializer(products, many=True)
        return Response(serializer.data)

    elif type == "delete":
        if user.is_staff:
            product = Product.objects.get(_id=pk)
            product.delete()
            return Response('Product Deleted')
        else:
            return Response({'detail': 'Not authorized to view this order'}, status=status.HTTP_400_BAD_REQUEST)

    elif type == "create":
        if user.is_staff:
            product = Product.objects.create(
                user=user,
                name='Sample Name',
                price=0,
                brand='Sample Brand',
                countInStock=0,
                category='Sample Category',
                description=''
            )
            serializer = ProductSerializer(product, many=False)
            return Response(serializer.data)
        else:
            return Response({'detail': 'Not authorized to view this order'}, status=status.HTTP_400_BAD_REQUEST)

    elif type == "update":
        if user.is_staff:
            if pk != "":
                product = Product.objects.get(_id=pk)
                product.name = data['name']
                product.price = data['price']
                product.brand = data['brand']
                product.countInStock = data['countInStock']
                product.category = data['category']
                product.description = data['description']
                product.save()
                serializer = ProductSerializer(product, many=False)
                return Response(serializer.data)
            else:
                return Response("Not found")
        else:
            return Response({'detail': 'Not authorized to view this order'}, status=status.HTTP_400_BAD_REQUEST)

    elif type == "createreview":
        if user != "AnonymousUser":
            product = Product.objects.get(_id=pk)
            # review already exists
            alreadyExists = product.review_set.filter(user=user).exists()
            if alreadyExists:
                content = {'detail': 'Product already reviewed'}
                return Response(content, status=status.HTTP_400_BAD_REQUEST)
            # no rating or 0
            elif data['rating'] == 0:
                content = {'detail': 'Please select a rating'}
                return Response(content, status=status.HTTP_400_BAD_REQUEST)
            # create review
            else:
                review = Review.objects.create(
                    user=user,
                    product=product,
                    name=user.first_name,
                    rating=data['rating'],
                    comment=data['comment'],
                )
                reviews = product.review_set.all()
                product.numReviews = len(reviews)
                total = 0
                for i in reviews:
                    total += i.rating
                product.rating = total / len(reviews)
                product.save()
                return Response('Review Added')
        else:
            content = {'detail': 'Please Login to write a review'}
            return Response(content, status=status.HTTP_400_BAD_REQUEST)
    else:
        return Response({}, status=status.HTTP_404_NOT_FOUND)
Exemplo n.º 17
0
 def post(self, request, format=None):
     data = json.loads(request.body.decode('utf-8'))
     res = SomeUser.create_user(**data)
     return Response(res.to_json())
Exemplo n.º 18
0
def getProduct(request, pk):
    product = Product.objects.get(_id=pk)
    serializer = ProductSerializer(product, many=False)
    return Response(serializer.data)
Exemplo n.º 19
0
 def put(self, request, pk, format=None):
     #TODO: not in prototype
     return Response({})
Exemplo n.º 20
0
def deleteProduct(request, pk):
    product = Product.objects.get(_id=pk)
    product.delete()
    return Response('Product Deleted')
Exemplo n.º 21
0
def info_view(request,pk=None):
    if request.method=='GET':
        qs = Info.objects.all()
        #ob =Info.objects.get(id=1) Info.objects.all().first() for single object
        
        #(method 1)
        #result=[]
        #for i in qs:
            # since we are getting all instances at once i.e. iterable objects so we use loop
        #    serializer = InfoSerializer(instance=i)
        #    result.append(serializer.data)
        #    return Response(result)
        
        #(method 2)
        serializer = InfoSerializer(instance=qs,many=True)
        # simply without writing loop we can pass another arugment many=True which will tell serializer that it is iterable object so you must write loop
        # serializer converts complex query to native python and then we convert it to json file
        # since we are taking data from database and converting to json format we can directly send the instance
        return Response(serializer.data)

    elif request.method=='POST':
        serializer = InfoSerializer(data=request.data)

        serializer.is_valid(raise_exception=True)
        
        #(method1: From views)
        #name = serializer.validated_data['name']
        #address= serializer.validated_data['address']

        #qs = Info.objects.create(
        #    name=name,
        #    address=address
        #    )
        #return Response({'msg':'ok post'}) 

        #(method2: From serializer)
        serializer.save()
        #without calling create from serializer as no instance is created it will automatically understand to go to create function 
        return Response({'msg':'ok post','result':serializer.data}) 

    elif request.method == "PUT":
        ## PUT is for full update and PATCH is for half update
        try:
            obj = Info.objects.get(pk=pk)
        except Info.DoesNotExist:
            return Response({'error':'Doesnot Exist'},status=404)

        #(method1: From views)
        #serializer = InfoSerializer(data=request.data)

        #serializer.is_valid(raise_exception=True)
        #name = serializer.validated_data['name']
        #address = serializer.validated_data['address']

        #obj.name = name
        #obj.address = address
        #obj.save()
        #return Response({'msg':'ok put'}) 

        #(method2:From serializer)
        serializer = InfoSerializer(data=request.data,instance=obj)
        #to hit update on serializer we must send instance attribute as well
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'msg':'ok put'}) 

    elif request.method == "DELETE":
        try:
            obj = Info.objects.get(pk=pk)
        except Info.DoesNotExist:
            return Response({'error':'Doesnot Exist'},status=404)

        obj.delete()
        return Response({'msg':'ok delete'}) 
Exemplo n.º 22
0
 def rate(self, request, pk):
     serializer = self.get_serializer(data=request.data)
     serializer.is_valid(raise_exception=True)
     CarRating.objects.create(car_id=pk,
                              rating=serializer.data.get('rating'))
     return Response(serializer.data, status='201')
Exemplo n.º 23
0
 def get(self, request):
     print("GET: {}".format(request))
     members = read_xls()
     print("members len: {}".format(len(members)))
     return Response(data=json.dumps(dump_members(members)),
                     status=status.HTTP_200_OK)
Exemplo n.º 24
0
 def list(self, request, count):
     queryset = models.Address.objects.values('postal_code').annotate(count=Count('company', distinct=True))\
         .filter(count__gte=count)
     serializer = serializers.PostalCodeSerializer(queryset, many=True)
     return Response(serializer.data)
Exemplo n.º 25
0
from fiber.models import Page, PageContentItem, ContentItem, File, Image
from fiber.app_settings import API_RENDER_HTML, PERMISSION_CLASS
from fiber.utils.import_util import load_class

from .serializers import PageSerializer, MovePageSerializer, PageContentItemSerializer, MovePageContentItemSerializer, ContentItemSerializer, FileSerializer, ImageSerializer, FiberPaginationSerializer

PERMISSIONS = load_class(PERMISSION_CLASS)

API_RENDERERS = (renderers.JSONRenderer, )
if API_RENDER_HTML:
    API_RENDERERS = (renderers.BrowsableAPIRenderer, renderers.JSONRenderer)

_403_FORBIDDEN_RESPONSE = Response(
    {
    'detail': 'You do not have permission to access this resource. ' +
    'You may need to login or otherwise authenticate the request.'
    },
    status.HTTP_403_FORBIDDEN)


class PlainText(renderers.BaseRenderer):
    media_type = 'text/plain'
    format = 'txt'

    def render(self, data, media_type=None, renderer_context=None):
        if isinstance(data, basestring):
            return data
        return smart_unicode(data)


class IEUploadFixMixin(object):
Exemplo n.º 26
0
    def get(self, request):

        kyc_list = KycInfo.objects.filter(verification_status=False)
        serializer = serializers.KycInfoSerializer(kyc_list, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
Exemplo n.º 27
0
 def check_fields(self, order_by):
     if order_by not in self.orderable_fields:
         return Response("Can not order by the passed value.", status=status.HTTP_400_BAD_REQUEST)
Exemplo n.º 28
0
    def put(self, request, project):
        """
        Update a Project
        ````````````````

        Update various attributes and configurable settings for the given
        project.  Only supplied values are updated.

        :pparam string organization_slug: the slug of the organization the
                                          project belongs to.
        :pparam string project_slug: the slug of the project to delete.
        :param string name: the new name for the project.
        :param string slug: the new slug for the project.
        :param boolean isBookmarked: in case this API call is invoked with a
                                     user context this allows changing of
                                     the bookmark flag.
        :param int digestsMinDelay:
        :param int digestsMaxDelay:
        :param object options: optional options to override in the
                               project settings.
        :auth: required
        """
        has_project_write = (
            (request.auth and request.auth.has_scope('project:write')) or
            (request.access and request.access.has_scope('project:write'))
        )

        if has_project_write:
            serializer_cls = ProjectAdminSerializer
        else:
            serializer_cls = ProjectMemberSerializer

        serializer = serializer_cls(data=request.DATA, partial=True)
        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        result = serializer.object

        changed = False
        if result.get('slug'):
            project.slug = result['slug']
            changed = True

        if result.get('name'):
            project.name = result['name']
            changed = True

        if changed:
            project.save()

        if result.get('isBookmarked'):
            try:
                with transaction.atomic():
                    ProjectBookmark.objects.create(
                        project_id=project.id,
                        user=request.user,
                    )
            except IntegrityError:
                pass
        elif result.get('isBookmarked') is False:
            ProjectBookmark.objects.filter(
                project_id=project.id,
                user=request.user,
            ).delete()

        if result.get('digestsMinDelay'):
            project.update_option('digests:mail:minimum_delay', result['digestsMinDelay'])
        if result.get('digestsMaxDelay'):
            project.update_option('digests:mail:maximum_delay', result['digestsMaxDelay'])
        if result.get('subjectPrefix'):
            project.update_option('mail:subject_prefix', result['subjectPrefix'])
        if result.get('subjectTemplate'):
            project.update_option('mail:subject_template', result['subjectTemplate'])

        if result.get('isSubscribed'):
            UserOption.objects.set_value(request.user, project, 'mail:alert', 1)
        elif result.get('isSubscribed') is False:
            UserOption.objects.set_value(request.user, project, 'mail:alert', 0)

        if has_project_write:
            options = request.DATA.get('options', {})
            if 'sentry:origins' in options:
                project.update_option(
                    'sentry:origins',
                    clean_newline_inputs(options['sentry:origins'])
                )
            if 'sentry:resolve_age' in options:
                project.update_option('sentry:resolve_age', int(options['sentry:resolve_age']))
            if 'sentry:scrub_data' in options:
                project.update_option('sentry:scrub_data', bool(options['sentry:scrub_data']))
            if 'sentry:scrub_defaults' in options:
                project.update_option('sentry:scrub_defaults', bool(options['sentry:scrub_defaults']))
            if 'sentry:safe_fields' in options:
                project.update_option(
                    'sentry:safe_fields',
                    [s.strip().lower() for s in options['sentry:safe_fields']]
                )
            if 'sentry:sensitive_fields' in options:
                project.update_option(
                    'sentry:sensitive_fields',
                    [s.strip().lower() for s in options['sentry:sensitive_fields']]
                )
            if 'sentry:csp_ignored_sources_defaults' in options:
                project.update_option('sentry:csp_ignored_sources_defaults', bool(options['sentry:csp_ignored_sources_defaults']))
            if 'sentry:csp_ignored_sources' in options:
                project.update_option(
                    'sentry:csp_ignored_sources',
                    clean_newline_inputs(options['sentry:csp_ignored_sources']))
            if 'feedback:branding' in options:
                project.update_option('feedback:branding', '1' if options['feedback:branding'] else '0')
            if 'sentry:reprocessing_active' in options:
                project.update_option('sentry:reprocessing_active',
                    bool(options['sentry:reprocessing_active']))
            if 'sentry:reprocessing_show_hint' in options:
                project.update_option('sentry:reprocessing_show_hint',
                    bool(options['sentry:reprocessing_show_hint']))
            if 'filters:blacklisted_ips' in options:
                project.update_option(
                    'sentry:blacklisted_ips',
                    clean_newline_inputs(options['filters:blacklisted_ips']))

            self.create_audit_entry(
                request=request,
                organization=project.organization,
                target_object=project.id,
                event=AuditLogEntryEvent.PROJECT_EDIT,
                data=project.get_audit_log_data(),
            )

        data = serialize(project, request.user)
        data['options'] = {
            'sentry:origins': '\n'.join(project.get_option('sentry:origins', ['*']) or []),
            'sentry:resolve_age': int(project.get_option('sentry:resolve_age', 0)),
        }
        data.update({
            'digestsMinDelay': project.get_option(
                'digests:mail:minimum_delay', digests.minimum_delay,
            ),
            'digestsMaxDelay': project.get_option(
                'digests:mail:maximum_delay', digests.maximum_delay,
            ),
            'subjectPrefix': project.get_option('mail:subject_prefix'),
            'subjectTemplate': project.get_option('mail:subject_template') or DEFAULT_SUBJECT_TEMPLATE.template,
        })

        return Response(data)
Exemplo n.º 29
0
 def get(self, request, format=None):
     """
     Get content groups data which is suitable for jqtree.
     """
     return Response(ContentItem.objects.get_content_groups(request.user))
Exemplo n.º 30
0
    def put(self, request, organization):
        """
        Bulk Mutate a List of Issues
        ````````````````````````````

        Bulk mutate various attributes on issues.  The list of issues
        to modify is given through the `id` query parameter.  It is repeated
        for each issue that should be modified.

        - For non-status updates, the `id` query parameter is required.
        - For status updates, the `id` query parameter may be omitted
          for a batch "update all" query.
        - An optional `status` query parameter may be used to restrict
          mutations to only events with the given status.

        The following attributes can be modified and are supplied as
        JSON object in the body:

        If any ids are out of scope this operation will succeed without
        any data mutation.

        :qparam int id: a list of IDs of the issues to be mutated.  This
                        parameter shall be repeated for each issue.  It
                        is optional only if a status is mutated in which
                        case an implicit `update all` is assumed.
        :qparam string status: optionally limits the query to issues of the
                               specified status.  Valid values are
                               ``"resolved"``, ``"unresolved"`` and
                               ``"ignored"``.
        :pparam string organization_slug: the slug of the organization the
                                          issues belong to.
        :param string status: the new status for the issues.  Valid values
                              are ``"resolved"``, ``"resolvedInNextRelease"``,
                              ``"unresolved"``, and ``"ignored"``. Status
                              updates that include release data are only allowed
                              for groups within a single project.
        :param map statusDetails: additional details about the resolution.
                                  Valid values are ``"inRelease"``, ``"inNextRelease"``,
                                  ``"inCommit"``,  ``"ignoreDuration"``, ``"ignoreCount"``,
                                  ``"ignoreWindow"``, ``"ignoreUserCount"``, and
                                  ``"ignoreUserWindow"``. Status detail
                                  updates that include release data are only allowed
                                  for groups within a single project.
        :param int ignoreDuration: the number of minutes to ignore this issue.
        :param boolean isPublic: sets the issue to public or private.
        :param boolean merge: allows to merge or unmerge different issues.
        :param string assignedTo: the user or team that should be assigned to
                                  these issues. Can be of the form ``"<user_id>"``,
                                  ``"user:<user_id>"``, ``"<username>"``,
                                  ``"<user_primary_email>"``, or ``"team:<team_id>"``.
                                  Bulk assigning issues is limited to groups
                                  within a single project.
        :param boolean hasSeen: in case this API call is invoked with a user
                                context this allows changing of the flag
                                that indicates if the user has seen the
                                event.
        :param boolean isBookmarked: in case this API call is invoked with a
                                     user context this allows changing of
                                     the bookmark flag.
        :auth: required
        """
        projects = self.get_projects(request, organization)
        if len(projects) > 1 and not features.has(
            "organizations:global-views", organization, actor=request.user
        ):
            return Response(
                {"detail": "You do not have the multi project stream feature enabled"}, status=400
            )

        search_fn = functools.partial(
            self._search,
            request,
            organization,
            projects,
            self.get_environments(request, organization),
        )
        return update_groups(request, projects, organization.id, search_fn)