Esempio n. 1
0
 def test_invalid_team_with_channel_timeout(self, mock_get_channel_id):
     self.setup_slack_integration()
     other_org = self.create_organization()
     new_team = self.create_team(organization=other_org)
     trigger = {
         "label":
         "critical",
         "alertThreshold":
         200,
         "actions": [
             {
                 "type": "slack",
                 "targetIdentifier": "my-channel",
                 "targetType": "specific",
                 "integration": self.integration.id,
             },
             {
                 "type": "email",
                 "targetType": "team",
                 "targetIdentifier": new_team.id
             },
         ],
     }
     base_params = self.valid_params.copy()
     base_params.update({"triggers": [trigger]})
     serializer = AlertRuleSerializer(context=self.context,
                                      data=base_params)
     # error is raised during save
     assert serializer.is_valid()
     with self.assertRaises(serializers.ValidationError) as err:
         serializer.save()
     assert err.exception.detail == {
         "nonFieldErrors": ["Team does not exist"]
     }
     mock_get_channel_id.assert_called_with(self.integration, "my-channel",
                                            10)
Esempio n. 2
0
 def test_decimal(self):
     params = self.valid_transaction_params.copy()
     alert_threshold = 0.8
     resolve_threshold = 0.7
     params["triggers"][0]["alertThreshold"] = alert_threshold
     params["triggers"][0]["resolveThreshold"] = resolve_threshold
     params["resolve_threshold"] = resolve_threshold
     # Drop off the warning trigger
     params["triggers"].pop()
     serializer = AlertRuleSerializer(context=self.context, data=params)
     assert serializer.is_valid(), serializer.errors
     alert_rule = serializer.save()
     trigger = alert_rule.alertruletrigger_set.filter(label="critical").get()
     assert trigger.alert_threshold == alert_threshold
     assert trigger.resolve_threshold == resolve_threshold
Esempio n. 3
0
    def new_alert_rule(self, data=None):
        if data is None:
            data = deepcopy(self.alert_rule_dict)

        serializer = AlertRuleSerializer(
            context={
                "organization": self.organization,
                "access": OrganizationGlobalAccess(self.organization),
            },
            data=data,
        )

        assert serializer.is_valid(), serializer.errors
        alert_rule = serializer.save()
        return alert_rule
Esempio n. 4
0
    def test_owner_validation(self):
        self.run_fail_validation_test(
            {"owner": f"meow:{self.user.id}"},
            {
                "owner": [
                    "Could not parse owner. Format should be `type:id` where type is `team` or `user`."
                ]
            },
        )
        self.run_fail_validation_test(
            {"owner": "user:1234567"},
            {"owner": ["Could not resolve owner to existing team or user."]},
        )
        self.run_fail_validation_test(
            {"owner": "team:1234567"},
            {"owner": ["Could not resolve owner to existing team or user."]},
        )
        base_params = self.valid_params.copy()
        base_params.update({"owner": f"team:{self.team.id}"})
        serializer = AlertRuleSerializer(context=self.context,
                                         data=base_params)
        assert serializer.is_valid(), serializer.errors
        alert_rule = serializer.save()
        assert alert_rule.owner == self.team.actor
        assert alert_rule.owner.type == ACTOR_TYPES["team"]

        base_params.update({
            "name": "another_test",
            "owner": f"user:{self.user.id}"
        })
        serializer = AlertRuleSerializer(context=self.context,
                                         data=base_params)
        assert serializer.is_valid(), serializer.errors
        alert_rule = serializer.save()
        assert alert_rule.owner == self.user.actor
        assert alert_rule.owner.type == ACTOR_TYPES["user"]
Esempio n. 5
0
 def test_event_types(self):
     invalid_values = [
         "Invalid event_type, valid values are %s" %
         [item.name.lower() for item in SnubaQueryEventType.EventType]
     ]
     self.run_fail_validation_test({"event_types": ["a"]},
                                   {"eventTypes": invalid_values})
     self.run_fail_validation_test({"event_types": [1]},
                                   {"eventTypes": invalid_values})
     self.run_fail_validation_test(
         {"event_types": ["transaction"]},
         {
             "nonFieldErrors": [
                 "Invalid event types for this dataset. Valid event types are ['default', 'error']"
             ]
         },
     )
     params = self.valid_params.copy()
     serializer = AlertRuleSerializer(context=self.context,
                                      data=params,
                                      partial=True)
     assert serializer.is_valid()
     alert_rule = serializer.save()
     assert set(alert_rule.snuba_query.event_types) == set(
         [SnubaQueryEventType.EventType.DEFAULT])
     params["event_types"] = [
         SnubaQueryEventType.EventType.ERROR.name.lower()
     ]
     serializer = AlertRuleSerializer(context=self.context,
                                      instance=alert_rule,
                                      data=params,
                                      partial=True)
     assert serializer.is_valid()
     alert_rule = serializer.save()
     assert set(alert_rule.snuba_query.event_types) == set(
         [SnubaQueryEventType.EventType.ERROR])
Esempio n. 6
0
    def test_environment(self):
        base_params = self.valid_params.copy()
        env_1 = Environment.objects.create(organization_id=self.organization.id, name="test_env_1")
        env_2 = Environment.objects.create(organization_id=self.organization.id, name="test_env_2")

        base_params.update({"environment": [env_1.name]})
        serializer = AlertRuleSerializer(context=self.context, data=base_params)
        assert serializer.is_valid()
        alert_rule = serializer.save()

        # Make sure AlertRuleEnvironment entry was made:
        alert_rule_env = AlertRuleEnvironment.objects.get(
            environment=env_1.id, alert_rule=alert_rule
        )
        assert alert_rule_env

        base_params.update({"id": alert_rule.id})
        base_params.update({"environment": [env_1.name, env_2.name]})
        serializer = AlertRuleSerializer(
            context=self.context, instance=alert_rule, data=base_params
        )
        assert serializer.is_valid()
        alert_rule = serializer.save()

        assert len(AlertRuleEnvironment.objects.filter(alert_rule=alert_rule)) == 2
        assert len(list(alert_rule.environment.all())) == 2

        base_params.update({"environment": [env_2.name]})
        serializer = AlertRuleSerializer(
            context=self.context, instance=alert_rule, data=base_params
        )
        assert serializer.is_valid()
        serializer.save()

        # Make sure env_1 AlertRuleEnvironment was deleted:
        try:
            alert_rule_env = AlertRuleEnvironment.objects.get(
                environment=env_1.id, alert_rule=alert_rule
            )
            assert False
        except AlertRuleEnvironment.DoesNotExist:
            assert True
        # And that env_2 is still present:
        assert len(AlertRuleEnvironment.objects.filter(alert_rule=alert_rule)) == 1
        assert (
            len(AlertRuleEnvironment.objects.filter(environment=env_2.id, alert_rule=alert_rule))
            == 1
        )

        base_params.update({"environment": []})
        serializer = AlertRuleSerializer(
            context=self.context, instance=alert_rule, data=base_params
        )
        assert serializer.is_valid()
        serializer.save()
        assert len(AlertRuleEnvironment.objects.filter(alert_rule=alert_rule)) == 0
Esempio n. 7
0
    def post(self, request, project):
        """
        Create an alert rule
        """
        if not features.has("organizations:incidents", project.organization, actor=request.user):
            raise ResourceDoesNotExist

        data = deepcopy(request.data)
        data["projects"] = [project.slug]

        serializer = AlertRuleSerializer(
            context={
                "organization": project.organization,
                "access": request.access,
                "user": request.user,
            },
            data=data,
        )

        if serializer.is_valid():
            try:
                alert_rule = serializer.save()
            except ChannelLookupTimeoutError:
                # need to kick off an async job for Slack
                client = tasks.RedisRuleStatus()
                task_args = {
                    "organization_id": project.organization_id,
                    "uuid": client.uuid,
                    "data": data,
                }
                tasks.find_channel_id_for_alert_rule.apply_async(kwargs=task_args)
                return Response({"uuid": client.uuid}, status=202)
            else:
                referrer = request.query_params.get("referrer")
                session_id = request.query_params.get("sessionId")
                alert_rule_created.send_robust(
                    user=request.user,
                    project=project,
                    rule=alert_rule,
                    rule_type="metric",
                    sender=self,
                    referrer=referrer,
                    session_id=session_id,
                    is_api_token=request.auth is not None,
                )
                return Response(serialize(alert_rule, request.user), status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    def put(self, request, organization, alert_rule):
        serializer = DrfAlertRuleSerializer(
            context={
                "organization": organization,
                "access": request.access
            },
            instance=alert_rule,
            data=request.data,
        )

        if serializer.is_valid():
            alert_rule = serializer.save()
            return Response(serialize(alert_rule, request.user),
                            status=status.HTTP_200_OK)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Esempio n. 9
0
    def post(self, request, organization):
        """
        Create an alert rule
        """

        if not features.has("organizations:incidents", organization, actor=request.user):
            raise ResourceDoesNotExist

        serializer = AlertRuleSerializer(
            context={"organization": organization, "access": request.access}, data=request.data
        )

        if serializer.is_valid():
            alert_rule = serializer.save()
            return Response(serialize(alert_rule, request.user), status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Esempio n. 10
0
def find_channel_id_for_alert_rule(organization_id,
                                   uuid,
                                   data,
                                   alert_rule_id=None):
    redis_rule_status = RedisRuleStatus(uuid)
    try:
        organization = Organization.objects.get(id=organization_id)
    except Organization.DoesNotExist:
        redis_rule_status.set_value("failed")
        return

    alert_rule = None
    if alert_rule_id:
        try:
            alert_rule = AlertRule.objects.get(organization_id=organization_id,
                                               id=alert_rule_id)
        except AlertRule.DoesNotExist:
            redis_rule_status.set_value("failed")
            return

    # we use SystemAccess here because we can't pass the access instance from the request into the task
    # this means at this point we won't raise any validation errors associated with permissions
    # however, we should only be calling this task after we tried saving the alert rule first
    # which will catch those kinds of validation errors
    serializer = AlertRuleSerializer(
        context={
            "organization": organization,
            "access": SystemAccess(),
            "use_async_lookup": True
        },
        data=data,
        instance=alert_rule,
    )
    if serializer.is_valid():
        try:
            alert_rule = serializer.save()
            redis_rule_status.set_value("success", alert_rule.id)
            return
        # we can still get a validation error for the channel not existing
        except (serializers.ValidationError, ChannelLookupTimeoutError):
            # channel doesn't exist error or validation error
            redis_rule_status.set_value("failed")
            return
    # some other error
    redis_rule_status.set_value("failed")
    return
Esempio n. 11
0
    def test_environment_non_list(self):
        base_params = self.valid_params.copy()
        env_1 = Environment.objects.create(
            organization_id=self.organization.id, name="test_env_1")
        Environment.objects.create(organization_id=self.organization.id,
                                   name="test_env_2")

        base_params.update({"environment": env_1.name})
        serializer = AlertRuleSerializer(context=self.context,
                                         data=base_params)
        assert serializer.is_valid(), serializer.errors
        alert_rule = serializer.save()

        # Make sure AlertRuleEnvironment entry was made:
        alert_rule_env = AlertRuleEnvironment.objects.get(
            environment=env_1.id, alert_rule=alert_rule)
        assert alert_rule_env
Esempio n. 12
0
 def test_comparison_delta_above(self):
     params = self.valid_params.copy()
     params["comparison_delta"] = 60
     params["resolveThreshold"] = 10
     params["triggers"][0]["alertThreshold"] = 50
     params["triggers"][1]["alertThreshold"] = 40
     serializer = AlertRuleSerializer(context=self.context,
                                      data=params,
                                      partial=True)
     assert serializer.is_valid(), serializer.errors
     alert_rule = serializer.save()
     assert alert_rule.comparison_delta == 60 * 60
     assert alert_rule.resolve_threshold == 110
     triggers = {
         trigger.label: trigger
         for trigger in alert_rule.alertruletrigger_set.all()
     }
     assert triggers["critical"].alert_threshold == 150
     assert triggers["warning"].alert_threshold == 140
     assert alert_rule.snuba_query.resolution == DEFAULT_CMP_ALERT_RULE_RESOLUTION * 60
Esempio n. 13
0
 def test_aggregate(self):
     self.run_fail_validation_test(
         {"aggregate": "what()"},
         {"aggregate": ["Invalid Metric: what() is not a valid function"]},
     )
     self.run_fail_validation_test(
         {"aggregate": "what"},
         {
             "nonFieldErrors": [
                 "Invalid Metric: Please pass a valid function for aggregation"
             ]
         },
     )
     self.run_fail_validation_test(
         {"aggregate": "123"},
         {
             "nonFieldErrors": [
                 "Invalid Metric: Please pass a valid function for aggregation"
             ]
         },
     )
     self.run_fail_validation_test(
         {"aggregate": "count_unique(123, hello)"},
         {
             "aggregate": [
                 "Invalid Metric: count_unique(123, hello): expected 1 arguments"
             ]
         },
     )
     self.run_fail_validation_test(
         {"aggregate": "max()"},
         {"aggregate": ["Invalid Metric: max(): expected 1 arguments"]})
     aggregate = "count_unique(tags[sentry:user])"
     base_params = self.valid_params.copy()
     base_params["aggregate"] = aggregate
     serializer = AlertRuleSerializer(context=self.context,
                                      data=base_params)
     assert serializer.is_valid(), serializer.errors
     alert_rule = serializer.save()
     assert alert_rule.snuba_query.aggregate == aggregate
Esempio n. 14
0
    def post(self, request, project):
        """
        Create an alert rule
        """
        if not features.has("organizations:incidents", project.organization, actor=request.user):
            raise ResourceDoesNotExist

        data = deepcopy(request.data)
        data["projects"] = [project.slug]

        serializer = AlertRuleSerializer(
            context={"organization": project.organization, "access": request.access}, data=data
        )

        if serializer.is_valid():
            alert_rule = serializer.save()
            alert_rule_created.send_robust(
                user=request.user, project=project, rule=alert_rule, rule_type="metric", sender=self
            )
            return Response(serialize(alert_rule, request.user), status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Esempio n. 15
0
def find_channel_id_for_alert_rule(organization_id,
                                   uuid,
                                   data,
                                   alert_rule_id=None,
                                   user_id=None):
    redis_rule_status = RedisRuleStatus(uuid)
    try:
        organization = Organization.objects.get(id=organization_id)
    except Organization.DoesNotExist:
        redis_rule_status.set_value("failed")
        return

    user = None
    if user_id:
        try:
            user = User.objects.get(id=user_id)
        except User.DoesNotExist:
            pass

    alert_rule = None
    if alert_rule_id:
        try:
            alert_rule = AlertRule.objects.get(organization_id=organization_id,
                                               id=alert_rule_id)
        except AlertRule.DoesNotExist:
            redis_rule_status.set_value("failed")
            return

    try:
        mapped_ids = get_slack_channel_ids(organization, user, data)
    except (serializers.ValidationError, ChannelLookupTimeoutError,
            InvalidTriggerActionError) as e:
        # channel doesn't exist error or validation error
        logger.info(
            "get_slack_channel_ids.failed",
            extra={
                "exception": e,
            },
        )
        redis_rule_status.set_value("failed")
        return

    for trigger in data["triggers"]:
        for action in trigger["actions"]:
            if action["type"] == "slack":
                if action["targetIdentifier"] in mapped_ids:
                    action["input_channel_id"] = mapped_ids[
                        action["targetIdentifier"]]
                else:
                    # We can early exit because we couldn't map this action's slack channel name to a slack id
                    # This is a fail safe, but I think we shouldn't really hit this.
                    redis_rule_status.set_value("failed")
                    return

    # we use SystemAccess here because we can't pass the access instance from the request into the task
    # this means at this point we won't raise any validation errors associated with permissions
    # however, we should only be calling this task after we tried saving the alert rule first
    # which will catch those kinds of validation errors
    serializer = AlertRuleSerializer(
        context={
            "organization": organization,
            "access": SystemAccess(),
            "user": user,
            "use_async_lookup": True,
            "validate_channel_id": False,
        },
        data=data,
        instance=alert_rule,
    )
    if serializer.is_valid():
        try:
            alert_rule = serializer.save()
            redis_rule_status.set_value("success", alert_rule.id)
            return
        # we can still get a validation error for the channel not existing
        except (serializers.ValidationError, ChannelLookupTimeoutError):
            # channel doesn't exist error or validation error
            redis_rule_status.set_value("failed")
            return
    # some other error
    redis_rule_status.set_value("failed")
    return
Esempio n. 16
0
 def test_transaction_dataset(self):
     serializer = AlertRuleSerializer(context=self.context, data=self.valid_transaction_params)
     assert serializer.is_valid(), serializer.errors
     alert_rule = serializer.save()
     assert alert_rule.snuba_query.dataset == QueryDatasets.TRANSACTIONS.value
     assert alert_rule.snuba_query.aggregate == "count()"