Beispiel #1
0
 def run_fail_validation_test(self, params, errors):
     base_params = self.valid_params.copy()
     base_params.update(params)
     serializer = AlertRuleSerializer(context=self.context,
                                      data=base_params)
     assert not serializer.is_valid()
     assert serializer.errors == errors
Beispiel #2
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()"
    def put(self, request, project, alert_rule):
        data = request.data
        serializer = DrfAlertRuleSerializer(
            context={
                "organization": project.organization,
                "access": request.access,
                "user": request.user,
            },
            instance=alert_rule,
            data=data,
            partial=True,
        )
        if serializer.is_valid():
            if get_slack_actions_with_async_lookups(project.organization,
                                                    request.user, data):
                # need to kick off an async job for Slack
                client = tasks.RedisRuleStatus()
                task_args = {
                    "organization_id": project.organization_id,
                    "uuid": client.uuid,
                    "data": data,
                    "alert_rule_id": alert_rule.id,
                    "user_id": request.user.id,
                }
                tasks.find_channel_id_for_alert_rule.apply_async(
                    kwargs=task_args)
                return Response({"uuid": client.uuid}, status=202)
            else:
                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)
Beispiel #4
0
 def test_aggregate_and_aggregation(self):
     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
Beispiel #5
0
 def _run_changed_fields_test(self, alert_rule, params, expected):
     serializer = AlertRuleSerializer(
         context=self.context, instance=alert_rule, data=params, partial=True
     )
     serializer.is_valid()
     assert (
         serializer._remove_unchanged_fields(alert_rule, serializer.validated_data) == expected
     )
Beispiel #6
0
 def test_valid_metric_field(self):
     base_params = self.valid_params.copy()
     base_params.update({"name": "Aun1qu3n4m3", "aggregate": "count_unique(user)"})
     serializer = AlertRuleSerializer(context=self.context, data=base_params)
     assert serializer.is_valid()
     serializer.save()
     assert len(list(AlertRule.objects.filter(name="Aun1qu3n4m3"))) == 1
     alert_rule = AlertRule.objects.filter(name="Aun1qu3n4m3").first()
     assert alert_rule.snuba_query.aggregate == "count_unique(tags[sentry:user])"
Beispiel #7
0
 def test_validation_no_params(self):
     serializer = AlertRuleSerializer(context=self.context, data={})
     assert not serializer.is_valid()
     field_is_required = ["This field is required."]
     assert serializer.errors == {
         "name": field_is_required,
         "timeWindow": field_is_required,
         "query": field_is_required,
     }
Beispiel #8
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")

        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()
        assert alert_rule.snuba_query.environment == env_1
Beispiel #9
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

    # 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,
        },
        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
Beispiel #10
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,
                    "user_id": request.user.id,
                }
                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)
Beispiel #11
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["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
Beispiel #12
0
 def test_validation_no_params(self):
     serializer = AlertRuleSerializer(context={"project": self.project},
                                      data={})
     assert not serializer.is_valid()
     field_is_required = ["This field is required."]
     assert serializer.errors == {
         "name": field_is_required,
         "timeWindow": field_is_required,
         "query": field_is_required,
         "thresholdType": field_is_required,
         "resolveThreshold": field_is_required,
         "alertThreshold": field_is_required,
     }
    def put(self, request, project, alert_rule):
        serializer = DrfAlertRuleSerializer(
            context={'project': project},
            instance=alert_rule,
            data=request.data,
            partial=True,
        )

        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)
Beispiel #14
0
 def test_validation_no_params(self):
     serializer = AlertRuleSerializer(context={'project': self.project},
                                      data={})
     assert not serializer.is_valid()
     field_is_required = ['This field is required.']
     assert serializer.errors == {
         'name': field_is_required,
         'timeWindow': field_is_required,
         'query': field_is_required,
         'thresholdType': field_is_required,
         'resolveThreshold': field_is_required,
         'alertThreshold': field_is_required,
         'aggregations': field_is_required,
     }
Beispiel #15
0
    def test_comparison_delta_below(self):
        params = self.valid_params.copy()
        params["threshold_type"] = AlertRuleThresholdType.BELOW.value
        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 == 90
        triggers = {
            trigger.label: trigger
            for trigger in alert_rule.alertruletrigger_set.all()
        }
        assert triggers["critical"].alert_threshold == 50
        assert triggers["warning"].alert_threshold == 60
        assert alert_rule.snuba_query.resolution == DEFAULT_CMP_ALERT_RULE_RESOLUTION * 60

        params["comparison_delta"] = None
        params["resolveThreshold"] = 100
        params["triggers"][0]["alertThreshold"] = 40
        params["triggers"][1]["alertThreshold"] = 50
        serializer = AlertRuleSerializer(context=self.context,
                                         instance=alert_rule,
                                         data=params,
                                         partial=True)
        assert serializer.is_valid(), serializer.errors
        alert_rule = serializer.save()
        assert alert_rule.comparison_delta is None
        assert alert_rule.snuba_query.resolution == DEFAULT_ALERT_RULE_RESOLUTION * 60
Beispiel #16
0
 def test_alert_rule_resolved_invalid(self):
     self.run_fail_validation_test(
         {"resolve_threshold": 500},
         {"nonFieldErrors": ["critical alert threshold must be above resolution threshold"]},
     )
     base_params = self.valid_params.copy()
     base_params["resolve_threshold"] = 0.5
     base_params["triggers"].pop()
     base_params["triggers"][0]["alertThreshold"] = 0.3
     serializer = AlertRuleSerializer(context=self.context, data=base_params)
     assert not serializer.is_valid()
     assert serializer.errors == {
         "nonFieldErrors": ["critical alert threshold must be above resolution threshold"]
     }
Beispiel #17
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"]
Beispiel #18
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) == {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) == {SnubaQueryEventType.EventType.ERROR}
Beispiel #19
0
    def _run_changed_fields_test(self, alert_rule, params, expected):
        test_params = self.valid_params.copy()
        test_params.update(params)

        expected.update({"triggers": self.Any(list)})
        serializer = AlertRuleSerializer(
            context=self.context, instance=alert_rule, data=test_params, partial=True
        )

        assert serializer.is_valid(), serializer.errors

        assert (
            serializer._remove_unchanged_fields(alert_rule, serializer.validated_data) == expected
        )
Beispiel #20
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")

        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
    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
    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)
Beispiel #23
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)
Beispiel #24
0
    def test_alert_rule_threshold_resolve_only(self):
        resolve_threshold = 10
        payload = {
            "name":
            "hello_im_a_test",
            "time_window":
            10,
            "query":
            "level:error",
            "aggregate":
            "count()",
            "thresholdType":
            0,
            "resolveThreshold":
            10,
            "threshold_period":
            1,
            "projects": [self.project.slug],
            "triggers": [{
                "label":
                "critical",
                "alertThreshold":
                98,
                "actions": [{
                    "type": "email",
                    "targetType": "team",
                    "targetIdentifier": self.team.id
                }],
            }],
        }
        serializer = AlertRuleSerializer(context=self.context,
                                         data=payload,
                                         partial=True)

        assert serializer.is_valid(), serializer.errors
        assert serializer.validated_data[
            "threshold_type"] == AlertRuleThresholdType.ABOVE
        assert serializer.validated_data[
            "resolve_threshold"] == resolve_threshold
        assert (serializer.validated_data["triggers"][0]["threshold_type"] ==
                AlertRuleThresholdType.ABOVE.value)
        assert serializer.validated_data["triggers"][0][
            "resolve_threshold"] == resolve_threshold
Beispiel #25
0
    def test_invalid_slack_channel(self):
        # We had an error where an invalid slack channel was spitting out unclear
        # error for the user, and CREATING THE RULE. So the next save (after fixing slack action)
        # says "Name already in use". This test makes sure that is not happening anymore.
        # We save a rule with an invalid slack, make sure we get back a useful error
        # and that the rule is not created.
        base_params = self.valid_params.copy()
        base_params["name"] = "Aun1qu3n4m3"
        integration = Integration.objects.create(
            external_id="1",
            provider="slack",
            metadata={
                "access_token": "xoxp-xxxxxxxxx-xxxxxxxxxx-xxxxxxxxxxxx"
            },
        )
        integration.add_organization(self.organization, self.user)
        base_params["triggers"][0]["actions"].append({
            "type":
            AlertRuleTriggerAction.get_registered_type(
                AlertRuleTriggerAction.Type.SLACK).slug,
            "targetType":
            action_target_type_to_string[
                AlertRuleTriggerAction.TargetType.SPECIFIC],
            "targetIdentifier":
            "123",
            "integration":
            six.text_type(integration.id),
        })
        serializer = AlertRuleSerializer(context=self.context,
                                         data=base_params)
        assert serializer.is_valid()
        with self.assertRaises(serializers.ValidationError):
            serializer.save()

        # Make sure the rule was not created.
        assert len(list(AlertRule.objects.filter(name="Aun1qu3n4m3"))) == 0

        # Make sure the action was not created.
        alert_rule_trigger_actions = list(
            AlertRuleTriggerAction.objects.filter(integration=integration))
        assert len(alert_rule_trigger_actions) == 0
    def test_boundary(self):
        payload = {
            "name":
            "hello_im_a_test",
            "time_window":
            10,
            "query":
            "level:error",
            "aggregate":
            "count()",
            "threshold_period":
            1,
            "resolveThreshold":
            2,
            "thresholdType":
            AlertRuleThresholdType.ABOVE.value,
            "projects": [self.project.slug],
            "triggers": [{
                "label":
                "critical",
                "alertThreshold":
                1,
                "actions": [{
                    "type": "email",
                    "targetType": "team",
                    "targetIdentifier": self.team.id
                }],
            }],
        }
        serializer = AlertRuleSerializer(context=self.context,
                                         data=payload,
                                         partial=True)

        assert serializer.is_valid(), serializer.errors

        # Now do a two trigger test:
        payload["triggers"].append({
            "label":
            "warning",
            "alertThreshold":
            0,
            "actions": [
                {
                    "type": "email",
                    "targetType": "team",
                    "targetIdentifier": self.team.id
                },
                {
                    "type": "email",
                    "targetType": "user",
                    "targetIdentifier": self.user.id
                },
            ],
        })

        serializer = AlertRuleSerializer(context=self.context,
                                         data=payload,
                                         partial=True)

        assert serializer.is_valid(), serializer.errors
    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 at most 1 argument(s)"
                ]
            },
        )
        self.run_fail_validation_test(
            {"aggregate": "max()"},
            {"aggregate": ["Invalid Metric: max(): expected 1 argument(s)"]})
        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

        aggregate = "sum(measurements.fp)"
        base_params = self.valid_transaction_params.copy()
        base_params["name"] = "measurement test"
        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
    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)
 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)
Beispiel #30
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