Exemple #1
0
 def test_invalid_team(self):
     trigger = AlertRuleTriggerAction(
         target_type=AlertRuleTriggerAction.TargetType.TEAM.value,
         target_identifier="10000000")
     assert trigger.target is None
Exemple #2
0
 def test_user(self):
     trigger = AlertRuleTriggerAction(
         target_type=AlertRuleTriggerAction.TargetType.USER.value,
         target_identifier=six.text_type(self.user.id),
     )
     assert trigger.target == self.user
Exemple #3
0
 def test_team(self):
     trigger = AlertRuleTriggerAction(
         target_type=AlertRuleTriggerAction.TargetType.TEAM.value,
         target_identifier=six.text_type(self.team.id),
     )
     assert trigger.target == self.team
Exemple #4
0
    AlertRule,
    AlertRuleThresholdType,
    AlertRuleTrigger,
    AlertRuleTriggerAction,
)
from sentry.models.organizationmember import OrganizationMember
from sentry.models.team import Team
from sentry.models.user import User
from sentry.snuba.models import QueryAggregations
from sentry.snuba.subscriptions import aggregation_function_translations
from sentry.utils.compat import zip


string_to_action_type = {
    registration.slug: registration.type
    for registration in AlertRuleTriggerAction.get_registered_types()
}
action_target_type_to_string = {
    AlertRuleTriggerAction.TargetType.USER: "******",
    AlertRuleTriggerAction.TargetType.TEAM: "team",
    AlertRuleTriggerAction.TargetType.SPECIFIC: "specific",
}
string_to_action_target_type = {v: k for (k, v) in action_target_type_to_string.items()}

CRITICAL_TRIGGER_LABEL = "critical"
WARNING_TRIGGER_LABEL = "warning"


class AlertRuleTriggerActionSerializer(CamelSnakeModelSerializer):
    """
    Serializer for creating/updating a trigger action. Required context:
Exemple #5
0
    def test_remove_unchanged_fields(self):
        type = AlertRuleTriggerAction.Type.EMAIL
        target_type = AlertRuleTriggerAction.TargetType.USER
        identifier = six.text_type(self.user.id)
        action = create_alert_rule_trigger_action(self.trigger, type,
                                                  target_type, identifier)

        self._run_changed_fields_test(
            action,
            {
                "type": AlertRuleTriggerAction.get_registered_type(type).slug,
                "target_type": action_target_type_to_string[target_type],
                "target_identifier": identifier,
            },
            {},
        )

        integration = Integration.objects.create(external_id="1",
                                                 provider="slack",
                                                 metadata={})

        self._run_changed_fields_test(
            action,
            {
                "type":
                AlertRuleTriggerAction.get_registered_type(
                    AlertRuleTriggerAction.Type.SLACK).slug,
                "targetIdentifier":
                identifier,
                "targetType":
                action_target_type_to_string[
                    AlertRuleTriggerAction.TargetType.SPECIFIC],
                "integration":
                integration.id,
            },
            {
                "type": AlertRuleTriggerAction.Type.SLACK,
                "integration": integration,
                "target_identifier": identifier,
                "target_type": AlertRuleTriggerAction.TargetType.SPECIFIC,
            },
        )

        new_team = self.create_team(self.organization)
        self._run_changed_fields_test(
            action,
            {
                "type":
                AlertRuleTriggerAction.get_registered_type(type).slug,
                "target_type":
                action_target_type_to_string[
                    AlertRuleTriggerAction.TargetType.TEAM],
                "target_identifier":
                six.text_type(new_team.id),
            },
            {
                "type": type,
                "target_type": AlertRuleTriggerAction.TargetType.TEAM,
                "target_identifier": six.text_type(new_team.id),
            },
        )
Exemple #6
0
 def test_no_handler(self):
     trigger = AlertRuleTriggerAction(type=AlertRuleTriggerAction.Type.EMAIL.value)
     assert trigger.fire(Mock(), Mock(), Mock(), 123) is None
Exemple #7
0
 def test_unhandled(self):
     trigger = AlertRuleTriggerAction(type=AlertRuleTriggerAction.Type.EMAIL.value)
     trigger.build_handler(Mock(), Mock(), Mock())
     self.metrics.incr.assert_called_once_with("alert_rule_trigger.unhandled_type.0")
Exemple #8
0
 def test_specific(self):
     email = "*****@*****.**"
     trigger = AlertRuleTriggerAction(
         target_type=AlertRuleTriggerAction.TargetType.SPECIFIC.value, target_identifier=email
     )
     assert trigger.target == email
Exemple #9
0
    def test_create_and_update_sentry_app_action_success(self):
        responses.add(
            method=responses.POST,
            url="https://example.com/sentry/alert-rule",
            status=200,
            json={},
        )

        serializer = AlertRuleTriggerActionSerializer(
            context=self.context,
            data={
                "type":
                AlertRuleTriggerAction.get_registered_type(
                    AlertRuleTriggerAction.Type.SENTRY_APP).slug,
                "target_type":
                action_target_type_to_string[
                    AlertRuleTriggerAction.TargetType.SENTRY_APP],
                "target_identifier":
                "1",
                "sentry_app":
                self.sentry_app.id,
                "sentry_app_config": {
                    "channel": "#general"
                },
                "sentry_app_installation_uuid":
                self.sentry_app_installation.uuid,
            },
        )
        assert serializer.is_valid()

        # Create action
        serializer.save()

        # # Make sure the action was created.
        alert_rule_trigger_actions = list(
            AlertRuleTriggerAction.objects.filter(sentry_app=self.sentry_app))
        assert len(alert_rule_trigger_actions) == 1

        # Update action
        serializer = AlertRuleTriggerActionSerializer(
            context=self.context,
            data={
                "type":
                AlertRuleTriggerAction.get_registered_type(
                    AlertRuleTriggerAction.Type.SENTRY_APP).slug,
                "target_type":
                action_target_type_to_string[
                    AlertRuleTriggerAction.TargetType.SENTRY_APP],
                "target_identifier":
                "1",
                "sentry_app":
                self.sentry_app.id,
                "sentry_app_config": {
                    "channel": "#announcements"
                },
                "sentry_app_installation_uuid":
                self.sentry_app_installation.uuid,
            },
            instance=alert_rule_trigger_actions[0],
        )

        assert serializer.is_valid()

        # Update action
        serializer.save()

        alert_rule_trigger_action = AlertRuleTriggerAction.objects.get(
            sentry_app=self.sentry_app)

        # Make sure the changes got applied
        assert alert_rule_trigger_action.sentry_app_config == {
            "channel": "#announcements"
        }
Exemple #10
0
    def validate(self, attrs):
        if ("type" in attrs) != ("target_type"
                                 in attrs) != ("target_identifier" in attrs):
            raise serializers.ValidationError(
                "type, targetType and targetIdentifier must be passed together"
            )
        type = attrs.get("type")
        target_type = attrs.get("target_type")
        access = self.context["access"]
        identifier = attrs.get("target_identifier")

        if type is not None:
            type_info = AlertRuleTriggerAction.get_registered_type(type)
            if target_type not in type_info.supported_target_types:
                allowed_target_types = ",".join(
                    ACTION_TARGET_TYPE_TO_STRING[type_name]
                    for type_name in type_info.supported_target_types)
                raise serializers.ValidationError({
                    "target_type":
                    "Invalid target type for %s. Valid types are [%s]" %
                    (type_info.slug, allowed_target_types)
                })

        if attrs.get("type") == AlertRuleTriggerAction.Type.EMAIL:
            if target_type == AlertRuleTriggerAction.TargetType.TEAM:
                try:
                    team = Team.objects.get(id=identifier)
                except Team.DoesNotExist:
                    raise serializers.ValidationError("Team does not exist")
                if not access.has_team(team):
                    raise serializers.ValidationError("Team does not exist")
            elif target_type == AlertRuleTriggerAction.TargetType.USER:
                try:
                    user = User.objects.get(id=identifier)
                except User.DoesNotExist:
                    raise serializers.ValidationError("User does not exist")

                if not OrganizationMember.objects.filter(
                        organization=self.context["organization"],
                        user=user).exists():
                    raise serializers.ValidationError(
                        "User does not belong to this organization")
        elif attrs.get("type") == AlertRuleTriggerAction.Type.SLACK:
            if not attrs.get("integration"):
                raise serializers.ValidationError(
                    {"integration": "Integration must be provided for slack"})

        elif attrs.get("type") == AlertRuleTriggerAction.Type.SENTRY_APP:
            if not attrs.get("sentry_app"):
                raise serializers.ValidationError({
                    "sentry_app":
                    "SentryApp must be provided for sentry_app"
                })
            if attrs.get("sentry_app_config"):
                if attrs.get("sentry_app_installation_uuid") is None:
                    raise serializers.ValidationError({
                        "sentry_app":
                        "Missing parameter: sentry_app_installation_uuid"
                    })

                try:
                    install = SentryAppInstallation.objects.get(
                        uuid=attrs.get("sentry_app_installation_uuid"))
                except SentryAppInstallation.DoesNotExist:
                    raise serializers.ValidationError(
                        {"sentry_app": "The installation does not exist."})
                # Check response from creator and bubble up errors from providers as a ValidationError
                result = alert_rule_actions.AlertRuleActionCreator.run(
                    install=install,
                    fields=attrs.get("sentry_app_config"),
                )

                if not result["success"]:
                    raise serializers.ValidationError(
                        {"sentry_app": result["message"]})

                del attrs["sentry_app_installation_uuid"]

        attrs["use_async_lookup"] = self.context.get("use_async_lookup")
        attrs["input_channel_id"] = self.context.get("input_channel_id")
        should_validate_channel_id = self.context.get("validate_channel_id",
                                                      True)
        # validate_channel_id is assumed to be true unless explicitly passed as false
        if attrs["input_channel_id"] and should_validate_channel_id:
            validate_channel_id(identifier, attrs["integration"].id,
                                attrs["input_channel_id"])
        return attrs
Exemple #11
0
class OrganizationAlertRuleAvailableActionIndexEndpointTest(APITestCase):
    endpoint = "sentry-api-0-organization-alert-rule-available-actions"
    email = AlertRuleTriggerAction.get_registered_type(
        AlertRuleTriggerAction.Type.EMAIL)
    slack = AlertRuleTriggerAction.get_registered_type(
        AlertRuleTriggerAction.Type.SLACK)
    sentry_app = AlertRuleTriggerAction.get_registered_type(
        AlertRuleTriggerAction.Type.SENTRY_APP)
    pagerduty = AlertRuleTriggerAction.get_registered_type(
        AlertRuleTriggerAction.Type.PAGERDUTY)

    def setUp(self):
        super().setUp()
        self.login_as(self.user)

    def install_new_sentry_app(self, name, **kwargs):
        kwargs.update(name=name,
                      organization=self.organization,
                      is_alertable=True,
                      verify_install=False)
        sentry_app = self.create_sentry_app(**kwargs)
        installation = self.create_sentry_app_installation(
            slug=sentry_app.slug,
            organization=self.organization,
            user=self.user)
        return installation

    def test_build_action_response_email(self):
        data = build_action_response(self.email)

        assert data["type"] == "email"
        assert sorted(data["allowedTargetTypes"]) == ["team", "user"]

    def test_build_action_response_slack(self):
        data = build_action_response(self.slack)

        assert data["type"] == "slack"
        assert data["allowedTargetTypes"] == ["specific"]

    def test_build_action_response_pagerduty(self):
        service_name = SERVICES[0]["service_name"]
        integration = Integration.objects.create(
            provider="pagerduty",
            name="Example PagerDuty",
            external_id="example-pagerduty",
            metadata={"services": SERVICES},
        )
        integration.add_organization(self.organization, self.user)
        service = PagerDutyService.objects.create(
            service_name=service_name,
            integration_key=SERVICES[0]["integration_key"],
            organization_integration=integration.organizationintegration_set.
            first(),
        )

        data = build_action_response(self.pagerduty,
                                     integration=integration,
                                     organization=self.organization)

        assert data["type"] == "pagerduty"
        assert data["allowedTargetTypes"] == ["specific"]
        assert data["options"] == [{
            "value": service.id,
            "label": service_name
        }]

    def test_build_action_response_sentry_app(self):
        installation = self.install_new_sentry_app("foo")

        data = build_action_response(self.sentry_app,
                                     sentry_app_installation=installation)

        assert data["type"] == "sentry_app"
        assert data["allowedTargetTypes"] == ["sentry_app"]
        assert data["status"] == SentryAppStatus.UNPUBLISHED_STR

    def test_no_integrations(self):
        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert response.data == [build_action_response(self.email)]

    def test_simple(self):
        integration = Integration.objects.create(external_id="1",
                                                 provider="slack")
        integration.add_organization(self.organization)

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 2
        assert build_action_response(self.email) in response.data
        assert (build_action_response(self.slack,
                                      integration=integration,
                                      organization=self.organization)
                in response.data)

    def test_duplicate_integrations(self):
        integration = Integration.objects.create(external_id="1",
                                                 provider="slack",
                                                 name="slack 1")
        integration.add_organization(self.organization)
        other_integration = Integration.objects.create(external_id="2",
                                                       provider="slack",
                                                       name="slack 2")
        other_integration.add_organization(self.organization)

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 3
        assert build_action_response(self.email) in response.data
        assert (build_action_response(self.slack,
                                      integration=integration,
                                      organization=self.organization)
                in response.data)
        assert (build_action_response(self.slack,
                                      integration=other_integration,
                                      organization=self.organization)
                in response.data)

    def test_no_feature(self):
        self.create_team(organization=self.organization, members=[self.user])
        self.get_error_response(self.organization.slug, status_code=404)

    def test_sentry_apps(self):
        installation = self.install_new_sentry_app("foo")

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 2
        assert build_action_response(self.email) in response.data
        assert (build_action_response(self.sentry_app,
                                      sentry_app_installation=installation)
                in response.data)

    def test_published_sentry_apps(self):
        # Should show up in available actions.
        installation = self.install_new_sentry_app("published", published=True)

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 2
        assert (build_action_response(self.sentry_app,
                                      sentry_app_installation=installation)
                in response.data)

    def test_no_ticket_actions(self):
        integration = Integration.objects.create(external_id="1",
                                                 provider="jira")
        integration.add_organization(self.organization)

        with self.feature([
                "organizations:incidents",
                "organizations:integrations-ticket-rules"
        ]):
            response = self.get_success_response(self.organization.slug)

        # There should be no ticket actions for Metric Alerts.
        assert len(response.data) == 1
        assert build_action_response(self.email) in response.data

    def test_integration_disabled(self):
        integration = Integration.objects.create(external_id="1",
                                                 provider="slack",
                                                 status=ObjectStatus.DISABLED)
        integration.add_organization(self.organization)

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 1
        assert build_action_response(self.email) in response.data

    def test_org_integration_disabled(self):
        integration = Integration.objects.create(external_id="1",
                                                 provider="slack")
        org_integration = integration.add_organization(self.organization)
        org_integration.update(status=ObjectStatus.DISABLED)

        with self.feature("organizations:incidents"):
            response = self.get_success_response(self.organization.slug)

        assert len(response.data) == 1
        assert build_action_response(self.email) in response.data
Exemple #12
0
    def validate(self, attrs):
        if ("type" in attrs) != ("target_type"
                                 in attrs) != ("target_identifier" in attrs):
            raise serializers.ValidationError(
                "type, targetType and targetIdentifier must be passed together"
            )
        type = attrs.get("type")
        target_type = attrs.get("target_type")
        access = self.context["access"]
        identifier = attrs.get("target_identifier")

        if type is not None:
            type_info = AlertRuleTriggerAction.get_registered_type(type)
            if target_type not in type_info.supported_target_types:
                allowed_target_types = ",".join([
                    action_target_type_to_string[type_name]
                    for type_name in type_info.supported_target_types
                ])
                raise serializers.ValidationError({
                    "target_type":
                    "Invalid target type for %s. Valid types are [%s]" %
                    (type_info.slug, allowed_target_types)
                })

        if attrs.get("type") == AlertRuleTriggerAction.Type.EMAIL:
            if target_type == AlertRuleTriggerAction.TargetType.TEAM:
                try:
                    team = Team.objects.get(id=identifier)
                except Team.DoesNotExist:
                    raise serializers.ValidationError("Team does not exist")
                if not access.has_team(team):
                    raise serializers.ValidationError("Team does not exist")
            elif target_type == AlertRuleTriggerAction.TargetType.USER:
                try:
                    user = User.objects.get(id=identifier)
                except User.DoesNotExist:
                    raise serializers.ValidationError("User does not exist")

                if not OrganizationMember.objects.filter(
                        organization=self.context["organization"],
                        user=user).exists():
                    raise serializers.ValidationError(
                        "User does not belong to this organization")
        elif attrs.get("type") == AlertRuleTriggerAction.Type.SLACK:
            if not attrs.get("integration"):
                raise serializers.ValidationError(
                    {"integration": "Integration must be provided for slack"})

        elif attrs.get("type") == AlertRuleTriggerAction.Type.SENTRY_APP:
            if not attrs.get("sentry_app"):
                raise serializers.ValidationError({
                    "sentry_app":
                    "SentryApp must be provided for sentry_app"
                })
        attrs["use_async_lookup"] = self.context.get("use_async_lookup")
        attrs["input_channel_id"] = self.context.get("input_channel_id")
        should_validate_channel_id = self.context.get("validate_channel_id",
                                                      True)
        # validate_channel_id is assumed to be true unless explicitly passed as false
        if attrs["input_channel_id"] and should_validate_channel_id:
            validate_channel_id(identifier, attrs["integration"].id,
                                attrs["input_channel_id"])
        return attrs