示例#1
0
    def get(self, request, organization, event_id):
        """
        Resolve a Event ID
        ``````````````````

        This resolves a event ID to the project slug and internal issue ID and internal event ID.

        :pparam string organization_slug: the slug of the organization the
                                          event ID should be looked up in.
        :param string event_id: the event ID to look up.
        :auth: required

        Return:
            organizationSlug
            projectSlug
            groupId
            eventId (optional)
        """

        # Largely copied from ProjectGroupIndexEndpoint
        if len(event_id) != 32:
            return Response({'detail': 'Event ID must be 32 characters.'},
                            status=400)

        project_slugs_by_id = dict(
            Project.objects.filter(organization=organization).values_list(
                'id', 'slug'))

        try:
            event = Event.objects.filter(
                event_id=event_id,
                project_id__in=project_slugs_by_id.keys())[0]
        except IndexError:
            try:
                event_mapping = EventMapping.objects.filter(
                    event_id=event_id,
                    project_id__in=project_slugs_by_id.keys())[0]

            except IndexError:
                raise ResourceDoesNotExist()

            return Response({
                'organizationSlug':
                organization.slug,
                'projectSlug':
                project_slugs_by_id[event_mapping.project_id],
                'groupId':
                six.text_type(event_mapping.group_id),
            })

        return Response({
            'organizationSlug': organization.slug,
            'projectSlug': project_slugs_by_id[event.project_id],
            'groupId': six.text_type(event.group_id),
            'eventId': six.text_type(event.id),
            'event': serialize(
                event,
                request.user,
            ),
        })
示例#2
0
    def get(self, request, organization, short_id):
        """
        Resolve a Short ID
        ``````````````````

        This resolves a short ID to the project slug and internal issue ID.

        :pparam string organization_slug: the slug of the organization the
                                          short ID should be looked up in.
        :pparam string short_id: the short ID to look up.
        :auth: required
        """
        try:
            group = Group.objects.by_qualified_short_id(organization.id, short_id)
        except Group.DoesNotExist:
            raise ResourceDoesNotExist()

        return Response(
            {
                "organizationSlug": organization.slug,
                "projectSlug": group.project.slug,
                "groupId": six.text_type(group.id),
                "group": serialize(group, request.user),
                "shortId": group.qualified_short_id,
            }
        )
示例#3
0
 def convert_args(self, request: Request, organization_slug, team_id, *args, **kwargs):
     args, kwargs = super().convert_args(request, organization_slug)
     try:
         kwargs["team"] = self._get_team(kwargs["organization"], team_id)
     except Team.DoesNotExist:
         raise ResourceDoesNotExist(detail=SCIM_404_GROUP_RES)
     return (args, kwargs)
    def get(self, request, organization, event_id):
        """
        Resolve a Event ID
        ``````````````````

        This resolves a event ID to the project slug and internal issue ID and internal event ID.

        :pparam string organization_slug: the slug of the organization the
                                          event ID should be looked up in.
        :param string event_id: the event ID to look up.
        :auth: required
        """
        # Largely copied from ProjectGroupIndexEndpoint
        if len(event_id) != 32:
            return Response({"detail": "Event ID must be 32 characters."},
                            status=400)

        # Limit to 100req/s
        if ratelimiter.is_limited(
                u"api:event-id-lookup:{}".format(
                    md5_text(request.user.id if request.user and request.user.
                             is_authenticated() else "").hexdigest()),
                limit=100,
                window=1,
        ):
            return Response(
                {
                    "detail":
                    "You are attempting to use this endpoint too quickly. Limit is 100 requests/second."
                },
                status=429,
            )

        project_slugs_by_id = dict(
            Project.objects.filter(organization=organization).values_list(
                "id", "slug"))

        try:
            snuba_filter = eventstore.Filter(
                conditions=[["event.type", "!=", "transaction"]],
                project_ids=project_slugs_by_id.keys(),
                event_ids=[event_id],
            )
            event = eventstore.get_events(filter=snuba_filter, limit=1)[0]
        except IndexError:
            raise ResourceDoesNotExist()
        else:
            return Response({
                "organizationSlug":
                organization.slug,
                "projectSlug":
                project_slugs_by_id[event.project_id],
                "groupId":
                six.text_type(event.group_id),
                "eventId":
                six.text_type(event.event_id),
                "event":
                serialize(event, request.user),
            })
示例#5
0
文件: teams.py 项目: blacknode/sentry
    def patch(self, request, organization, team):
        """
        A SCIM Group PATCH request takes a series of operations to perform on a team.
        It does them sequentially and if any of them fail no operations should go through.
        The operations are add members, remove members, replace members, and rename team.
        """
        operations = request.data.get("Operations", [])
        if len(operations) > 100:
            return Response(SCIM_400_TOO_MANY_PATCH_OPS_ERROR, status=400)
        try:
            with transaction.atomic():
                for operation in operations:
                    op = operation["op"].lower()
                    if op == TeamPatchOps.ADD and operation[
                            "path"] == "members":
                        self._add_members_operation(request, operation, team)
                    elif op == TeamPatchOps.REMOVE and "members" in operation[
                            "path"]:
                        # the members op contains a filter string like so:
                        # members[userName eq "*****@*****.**"]
                        self._remove_members_operation(request, operation,
                                                       team)
                    elif op == TeamPatchOps.REPLACE:
                        path = operation.get("path")

                        if path == "members":
                            # delete all the current team members
                            # and replace with the ones in the operation list
                            with transaction.atomic():
                                queryset = OrganizationMemberTeam.objects.filter(
                                    team_id=team.id)
                                queryset.delete()
                                self._add_members_operation(
                                    request, operation, team)
                        # azure and okta handle team name change operation differently
                        elif path is None:
                            # for okta
                            self._rename_team_operation(
                                request, operation["value"]["displayName"],
                                team)
                        elif path == "displayName":
                            # for azure
                            self._rename_team_operation(
                                request, operation["value"], team)
                        else:
                            return Response(SCIM_400_UNSUPPORTED_ATTRIBUTE,
                                            status=400)

        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist(detail=SCIM_404_USER_RES)
        except IntegrityError as e:
            sentry_sdk.capture_exception(e)
            return Response(SCIM_400_INTEGRITY_ERROR, status=400)

        context = serialize(team, serializer=TeamSCIMSerializer())
        return Response(context)
示例#6
0
    def post(self, request: Request, organization) -> Response:
        if not self.has_feature(request, organization):
            return Response(status=404)
        logger.info("discover1.request", extra={"organization_id": organization.id})

        try:
            requested_projects = set(map(int, request.data.get("projects", [])))
        except (ValueError, TypeError):
            raise ResourceDoesNotExist()
        projects = self._get_projects_by_id(requested_projects, request, organization)

        serializer = DiscoverQuerySerializer(data=request.data)

        if not serializer.is_valid():
            return Response(serializer.errors, status=400)

        serialized = serializer.validated_data

        has_aggregations = len(serialized.get("aggregations")) > 0

        selected_columns = (
            serialized.get("conditionFields", []) + []
            if has_aggregations
            else serialized.get("fields", [])
        )

        projects_map = {}
        for project in projects:
            projects_map[project.id] = project.slug

        # Make sure that all selected fields are in the group by clause if there
        # are aggregations
        groupby = serialized.get("groupby") or []
        fields = serialized.get("fields") or []
        if has_aggregations:
            for field in fields:
                if field not in groupby:
                    groupby.append(field)

        return self.do_query(
            projects=projects_map,
            start=serialized.get("start"),
            end=serialized.get("end"),
            groupby=groupby,
            selected_columns=selected_columns,
            conditions=serialized.get("conditions"),
            orderby=serialized.get("orderby"),
            limit=serialized.get("limit"),
            aggregations=serialized.get("aggregations"),
            rollup=serialized.get("rollup"),
            filter_keys={"project.id": list(projects_map.keys())},
            arrayjoin=serialized.get("arrayjoin"),
            request=request,
            turbo=serialized.get("turbo"),
        )
示例#7
0
    def get(self, request: Request, organization, metric_name) -> Response:
        if not features.has(
                "organizations:metrics", organization, actor=request.user):
            return Response(status=404)

        projects = self.get_projects(request, organization)
        try:
            metric = get_single_metric_info(projects, metric_name)
        except InvalidParams:
            raise ResourceDoesNotExist(detail=f"metric '{metric_name}'")

        return Response(metric, status=200)
示例#8
0
    def get(self, request, project, metric_name):

        if not features.has("organizations:metrics",
                            project.organization,
                            actor=request.user):
            return Response(status=404)

        try:
            metric = DATA_SOURCE.get_single_metric(project, metric_name)
        except InvalidParams:
            raise ResourceDoesNotExist(detail=f"metric '{metric_name}'")

        return Response(metric, status=200)
    def get(self, request: Request, project, event_id) -> Response:
        """
        Returns the grouping information for an event
        `````````````````````````````````````````````

        This endpoint returns a JSON dump of the metadata that went into the
        grouping algorithm.
        """
        event = eventstore.get_event_by_id(project.id, event_id)
        if event is None:
            raise ResourceDoesNotExist

        rv = {}
        config_name = request.GET.get("config") or None

        # We always fetch the stored hashes here.  The reason for this is
        # that we want to show in the UI if the forced grouping algorithm
        # produced hashes that would normally also appear in the event.
        hashes = event.get_hashes()

        try:
            variants = event.get_grouping_variants(force_config=config_name,
                                                   normalize_stacktraces=True)
        except GroupingConfigNotFound:
            raise ResourceDoesNotExist(detail="Unknown grouping config")

        for (key, variant) in variants.items():
            d = variant.as_dict()
            # Since the hashes are generated on the fly and might no
            # longer match the stored ones we indicate if the hash
            # generation caused the hash to mismatch.
            d["hashMismatch"] = hash_mismatch = (
                d["hash"] is not None and d["hash"] not in hashes.hashes
                and d["hash"] not in hashes.hierarchical_hashes)

            if hash_mismatch:
                metrics.incr("event_grouping_info.hash_mismatch")
                logger.error(
                    "event_grouping_info.hash_mismatch",
                    extra={
                        "project_id": project.id,
                        "event_id": event_id
                    },
                )
            else:
                metrics.incr("event_grouping_info.hash_match")

            d["key"] = key
            rv[key] = d

        return HttpResponse(json.dumps(rv), content_type="application/json")
示例#10
0
    def get(self, request: Request, organization, metric_name) -> Response:
        if not features.has(
                "organizations:metrics", organization, actor=request.user):
            return Response(status=404)

        projects = self.get_projects(request, organization)
        try:
            metric = get_single_metric_info(projects, metric_name)
        except InvalidParams as e:
            raise ResourceDoesNotExist(e)
        except DerivedMetricParseException as exc:
            raise ParseError(detail=str(exc))

        return Response(metric, status=200)
示例#11
0
    def get(self, request, project, event_id):
        """
        Returns the grouping information for an event
        `````````````````````````````````````````````

        This endpoint returns a JSON dump of the metadata that went into the
        grouping algorithm.
        """

        use_snuba = options.get('snuba.events-queries.enabled')

        event_cls = event_cls = SnubaEvent if use_snuba else Event

        event = event_cls.objects.from_event_id(event_id,
                                                project_id=project.id)
        if event is None:
            raise ResourceDoesNotExist

        Event.objects.bind_nodes([event], 'data')

        rv = {}
        config_name = request.GET.get('config') or None

        # We always fetch the stored hashes here.  The reason for this is
        # that we want to show in the UI if the forced grouping algorithm
        # produced hashes that would normally also appear in the event.
        hashes = event.get_hashes()

        try:
            variants = event.get_grouping_variants(force_config=config_name,
                                                   normalize_stacktraces=True)
        except GroupingConfigNotFound:
            raise ResourceDoesNotExist(detail='Unknown grouping config')

        for (key, variant) in six.iteritems(variants):
            d = variant.as_dict()
            # Since the hashes are generated on the fly and might no
            # longer match the stored ones we indicate if the hash
            # generation caused the hash to mismatch.
            d['hashMismatch'] = d['hash'] is not None and d[
                'hash'] not in hashes
            d['key'] = key
            rv[key] = d

        return HttpResponse(json.dumps(rv), content_type='application/json')
示例#12
0
    def get(self, request, organization, event_id):
        """
        Resolve a Event ID
        ``````````````````

        This resolves a event ID to the project slug and internal issue ID and internal event ID.

        :pparam string organization_slug: the slug of the organization the
                                          event ID should be looked up in.
        :param string event_id: the event ID to look up.
        :auth: required
        """
        # Largely copied from ProjectGroupIndexEndpoint
        if len(event_id) != 32:
            return Response({"detail": "Event ID must be 32 characters."},
                            status=400)

        project_slugs_by_id = dict(
            Project.objects.filter(organization=organization).values_list(
                "id", "slug"))

        try:
            event = eventstore.get_events(
                filter_keys={
                    "project_id": project_slugs_by_id.keys(),
                    "event_id": event_id
                },
                limit=1,
            )[0]
        except IndexError:
            raise ResourceDoesNotExist()
        else:
            return Response({
                "organizationSlug":
                organization.slug,
                "projectSlug":
                project_slugs_by_id[event.project_id],
                "groupId":
                six.text_type(event.group_id),
                "eventId":
                six.text_type(event.id),
                "event":
                serialize(event, request.user),
            })
    def get(self, request: Request, organization, event_id) -> Response:
        """
        Resolve an Event ID
        ``````````````````

        This resolves an event ID to the project slug and internal issue ID and internal event ID.

        :pparam string organization_slug: the slug of the organization the
                                          event ID should be looked up in.
        :param string event_id: the event ID to look up. validated by a
                                regex in the URL.
        :auth: required
        """
        if event_id and not is_event_id(event_id):
            return Response({"detail": INVALID_ID_DETAILS.format("Event ID")},
                            status=400)

        project_slugs_by_id = dict(
            Project.objects.filter(organization=organization).values_list(
                "id", "slug"))

        try:
            snuba_filter = eventstore.Filter(
                conditions=[["event.type", "!=", "transaction"]],
                project_ids=list(project_slugs_by_id.keys()),
                event_ids=[event_id],
            )
            event = eventstore.get_events(filter=snuba_filter, limit=1)[0]
        except IndexError:
            raise ResourceDoesNotExist()
        else:
            return Response({
                "organizationSlug":
                organization.slug,
                "projectSlug":
                project_slugs_by_id[event.project_id],
                "groupId":
                str(event.group_id),
                "eventId":
                str(event.event_id),
                "event":
                serialize(event, request.user),
            })
示例#14
0
    def get(self, request: Request, organization, tag_name) -> Response:

        if not features.has(
                "organizations:metrics", organization, actor=request.user):
            return Response(status=404)

        metric_names = request.GET.getlist("metric") or None

        projects = self.get_projects(request, organization)
        try:
            tag_values = get_tag_values(projects, tag_name, metric_names)
        except (InvalidParams, DerivedMetricParseException) as exc:
            msg = str(exc)
            # TODO: Use separate error type once we have real data
            if "Unknown tag" in msg:
                raise ResourceDoesNotExist(f"tag '{tag_name}'")
            else:
                raise ParseError(msg)

        return Response(tag_values, status=200)
示例#15
0
    def get(self, request, project, tag_name):

        if not features.has("organizations:metrics",
                            project.organization,
                            actor=request.user):
            return Response(status=404)

        metric_names = request.GET.getlist("metric") or None

        try:
            tag_values = DATA_SOURCE.get_tag_values(project, tag_name,
                                                    metric_names)
        except InvalidParams as exc:
            msg = str(exc)
            # TODO: Use separate error type once we have real data
            if "Unknown tag" in msg:
                raise ResourceDoesNotExist(f"tag '{tag_name}'")
            else:
                raise ParseError(msg)

        return Response(tag_values, status=200)
示例#16
0
    def get(self, request, organization, short_id):
        """
        Resolve a Short ID
        ``````````````````

        This resolves a short ID to the project slug and internal issue ID.

        :pparam string organization_slug: the slug of the organization the
                                          short ID should be looked up in.
        :pparam string short_id: the short ID to look up.
        :auth: required
        """
        try:
            group = Group.objects.by_qualified_short_id(organization, short_id)
        except Group.DoesNotExist:
            raise ResourceDoesNotExist()

        return Response({
            'organizationSlug': organization.slug,
            'projectSlug': group.project.slug,
            'groupId': str(group.id),
            'shortId': group.qualified_short_id,
        })
 def get(self, request, organization, work_batch_type):
     if work_batch_type not in self.app.plugins.handlers_mapped_by_work_batch_type:
         raise ResourceDoesNotExist()
     ret = self.app.plugins.handlers_mapped_by_work_batch_type[
         work_batch_type]
     return Response(serialize(ret))
示例#18
0
    def patch(self, request: Request, organization, team):
        """
        A SCIM Group PATCH request takes a series of operations to perform on a team.
        It does them sequentially and if any of them fail no operations should go through.
        The operations are add members, remove members, replace members, and rename team.
        Update a team's attributes with a SCIM Group PATCH Request. Valid Operations are:
        * Renaming a team:
        ```json
        {
            "op": "replace",
            "value": {
                "id": 23,
                "displayName": "newName"
            }
        }
        ```
        * Adding a member to a team:
        ```json
        {
            "op": "add",
            "path": "members",
            "value": [
                {
                    "value": 23,
                    "display": "*****@*****.**"
                }
            ]
        }
        ```
        * Removing a member from a team:
        ```json
        {
            "op": "remove",
            "path": "members[value eq \"23\"]"
        }
        ```
        * Replacing an entire member set of a team:
        ```json
        {
            "op": "replace",
            "path": "members",
            "value": [
                {
                    "value": 23,
                    "display": "*****@*****.**"
                },
                {
                    "value": 24,
                    "display": "*****@*****.**"
                }
            ]
        }
        ```
        """
        operations = request.data.get("Operations", [])
        if len(operations) > 100:
            return Response(SCIM_400_TOO_MANY_PATCH_OPS_ERROR, status=400)
        try:
            with transaction.atomic():
                for operation in operations:
                    op = operation["op"].lower()
                    if op == TeamPatchOps.ADD and operation["path"] == "members":
                        self._add_members_operation(request, operation, team)
                    elif op == TeamPatchOps.REMOVE and "members" in operation["path"]:
                        self._remove_members_operation(
                            request, self._get_member_id_for_remove_op(operation), team
                        )
                    elif op == TeamPatchOps.REPLACE:
                        path = operation.get("path")

                        if path == "members":
                            # delete all the current team members
                            # and replace with the ones in the operation list
                            with transaction.atomic():
                                queryset = OrganizationMemberTeam.objects.filter(team_id=team.id)
                                queryset.delete()
                                self._add_members_operation(request, operation, team)
                        # azure and okta handle team name change operation differently
                        elif path is None:
                            # for okta
                            self._rename_team_operation(
                                request, operation["value"]["displayName"], team
                            )
                        elif path == "displayName":
                            # for azure
                            self._rename_team_operation(request, operation["value"], team)
                        else:
                            return Response(SCIM_400_UNSUPPORTED_ATTRIBUTE, status=400)

        except OrganizationMember.DoesNotExist:
            raise ResourceDoesNotExist(detail=SCIM_404_USER_RES)
        except IntegrityError as e:
            sentry_sdk.capture_exception(e)
            return Response(SCIM_400_INTEGRITY_ERROR, status=400)

        return self.respond(status=204)