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)
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
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 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"]
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])
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
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)
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)
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
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
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
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
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 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
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()"