def setUp(self): super(OrganizationAlertRuleAvailableActionIndexEndpointTest, self).setUp() self.login_as(self.user) self.email = AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.EMAIL) self.slack = AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SLACK)
def test_slack(self): self.run_fail_validation_test( { "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SLACK).slug, "target_type": action_target_type_to_string[ AlertRuleTriggerAction.TargetType.USER], "target_identifier": "123", }, { "targetType": ["Invalid target type for slack. Valid types are [specific]"] }, ) self.run_fail_validation_test( { "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SLACK).slug, "targetType": action_target_type_to_string[ AlertRuleTriggerAction.TargetType.SPECIFIC], "targetIdentifier": "123", }, {"integration": ["Integration must be provided for slack"]}, ) 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 = self.valid_params.copy() base_params.update({ "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 = AlertRuleTriggerActionSerializer(context=self.context, data=base_params) assert serializer.is_valid() with self.assertRaises(serializers.ValidationError): serializer.save()
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), }, )
def serialize(self, obj, attrs, user): from sentry.incidents.endpoints.serializers import action_target_type_to_string return { "id": str(obj.id), "alertRuleTriggerId": str(obj.alert_rule_trigger_id), "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(obj.type)).slug, "targetType": action_target_type_to_string[AlertRuleTriggerAction.TargetType( obj.target_type)], "targetIdentifier": self.get_identifier_from_action(obj), "integrationId": obj.integration_id, "sentryAppId": obj.sentry_app_id, "dateCreated": obj.date_added, "desc": self.human_desc(obj), }
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
def serialize(self, obj, attrs, user, **kwargs): from sentry.incidents.serializers import ACTION_TARGET_TYPE_TO_STRING result = { "id": str(obj.id), "alertRuleTriggerId": str(obj.alert_rule_trigger_id), "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(obj.type)).slug, "targetType": ACTION_TARGET_TYPE_TO_STRING[AlertRuleTriggerAction.TargetType( obj.target_type)], "targetIdentifier": self.get_identifier_from_action(obj), "inputChannelId": self.get_input_channel_id(obj), "integrationId": obj.integration_id, "sentryAppId": obj.sentry_app_id, "dateCreated": obj.date_added, "desc": self.human_desc(obj), } # Check if action is a Sentry App that has Alert Rule UI Component settings if obj.sentry_app_id and obj.sentry_app_config: result["settings"] = obj.sentry_app_config return result
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(), ) pagerduty = AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.PAGERDUTY) data = build_action_response(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_sentry_app_action_creator_fails(self): responses.add( method=responses.POST, url="https://example.com/sentry/alert-rule", status=400, body="Invalid channel.", ) self.run_fail_validation_test( { "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": "#santry" }, "sentry_app_installation_uuid": self.sentry_app_installation.uuid, }, {"sentryApp": ["Super Awesome App: Invalid channel."]}, )
def valid_params(self): return { "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.EMAIL ).slug, "target_type": action_target_type_to_string[AlertRuleTriggerAction.TargetType.SPECIFIC], "target_identifier": "*****@*****.**", }
def assert_action_serialized(self, action, result): assert result["id"] == six.text_type(action.id) assert result["alertRuleTriggerId"] == six.text_type( action.alert_rule_trigger_id) assert (result["type"] == AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(action.type)).slug) assert (result["targetType"] == action_target_type_to_string[ AlertRuleTriggerAction.TargetType(action.target_type)]) assert result["targetIdentifier"] == action.target_identifier assert result["integrationId"] == action.integration_id assert result["dateCreated"] == action.date_added
def valid_params(self): return { "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.EMAIL).slug, "target_type": ACTION_TARGET_TYPE_TO_STRING[ AlertRuleTriggerAction.TargetType.SPECIFIC], "target_identifier": "*****@*****.**", }
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"} ) return attrs
def test_valid_slack_channel_id(self): """ Test that when a valid Slack channel ID is provided, we look up the channel name and validate it against the targetIdentifier. """ 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 = self.valid_params.copy() base_params.update({ "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SLACK).slug, "targetType": action_target_type_to_string[ AlertRuleTriggerAction.TargetType.SPECIFIC], "targetIdentifier": "merp", "integration": str(integration.id), }) context = self.context.copy() context.update({"input_channel_id": "CSVK0921"}) responses.add( method=responses.GET, url="https://slack.com/api/conversations.info", status=200, content_type="application/json", body=json.dumps({ "ok": "true", "channel": { "name": "merp", "id": "CSVK0921" } }), ) serializer = AlertRuleTriggerActionSerializer(context=context, data=base_params) assert serializer.is_valid() serializer.save() # # Make sure the action was created. alert_rule_trigger_actions = list( AlertRuleTriggerAction.objects.filter(integration=integration)) assert len(alert_rule_trigger_actions) == 1
def test_invalid_slack_channel_name(self): """ Test that an invalid Slack channel name is detected and blocks the action from being saved. """ 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 = self.valid_params.copy() base_params.update({ "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SLACK).slug, "targetType": ACTION_TARGET_TYPE_TO_STRING[ AlertRuleTriggerAction.TargetType.SPECIFIC], "targetIdentifier": "123", "integration": str(integration.id), }) context = self.context.copy() context.update({"input_channel_id": "CSVK0921"}) responses.add( method=responses.GET, url="https://slack.com/api/conversations.info", status=200, content_type="application/json", body=json.dumps({ "ok": "true", "channel": { "name": "merp", "id": "CSVK0921" } }), ) serializer = AlertRuleTriggerActionSerializer(context=context, data=base_params) assert not serializer.is_valid() # # 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_sentry_apps(self): sentry_app = self.install_new_sentry_app("foo") with self.feature( ["organizations:incidents", "organizations:integrations-sentry-app-metric-alerts"] ): resp = self.get_valid_response(self.organization.slug) assert resp.data == [ build_action_response( AlertRuleTriggerAction.get_registered_type(AlertRuleTriggerAction.Type.SENTRY_APP), sentry_app=sentry_app, ), build_action_response(self.email), ]
def test_blocked_sentry_apps(self): internal_sentry_app = self.install_new_sentry_app("internal") # Should not show up in available actions. self.install_new_sentry_app("published", published=True) with self.feature( ["organizations:incidents", "organizations:integrations-sentry-app-metric-alerts"] ): resp = self.get_valid_response(self.organization.slug) assert resp.data == [ build_action_response( AlertRuleTriggerAction.get_registered_type(AlertRuleTriggerAction.Type.SENTRY_APP), sentry_app=internal_sentry_app, ), build_action_response(self.email), ]
def serialize(self, obj, attrs, user): from sentry.incidents.endpoints.serializers import action_target_type_to_string return { "id": six.text_type(obj.id), "alertRuleTriggerId": six.text_type(obj.alert_rule_trigger_id), "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(obj.type) ).slug, "targetType": action_target_type_to_string[ AlertRuleTriggerAction.TargetType(obj.target_type) ], "targetIdentifier": obj.target_display if obj.target_display is not None else obj.target_identifier, "integrationId": obj.integration_id, "dateCreated": obj.date_added, }
def test_sentry_app_action_missing_params(self): self.run_fail_validation_test( { "type": AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.SENTRY_APP).slug, "target_type": action_target_type_to_string[ AlertRuleTriggerAction.TargetType.SENTRY_APP], "target_identifier": "123", "sentry_app": self.sentry_app.id, "sentry_app_config": { "tag": "asdfasdfads" }, }, {"sentryApp": ["Missing paramater: sentry_app_installation_uuid"]}, )
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_simple(self): self.create_member(user=self.user, organization=self.organization, role="owner", teams=[self.team]) self.login_as(self.user) with self.feature("organizations:incidents"): resp = self.get_valid_response( self.organization.slug, self.alert_rule.id, self.trigger.id, type=AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type.EMAIL).slug, target_type=action_target_type_to_string[ AlertRuleTriggerAction.TargetType.USER], target_identifier=six.text_type(self.user.id), status_code=201, ) assert "id" in resp.data action = AlertRuleTriggerAction.objects.get(id=resp.data["id"]) assert resp.data == serialize(action, self.user)
def test_not_updated_fields(self): self.create_member(user=self.user, organization=self.organization, role="owner", teams=[self.team]) self.login_as(self.user) with self.feature("organizations:incidents"): resp = self.get_valid_response( self.organization.slug, self.alert_rule.id, self.trigger.id, self.action.id, type=AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(self.action.type)).slug, targetType=action_target_type_to_string[ AlertRuleTriggerAction.TargetType( self.action.target_type)], targetIdentifier=self.action.target_identifier, ) # Alert rule should be exactly the same assert resp.data == serialize(self.action)
def test_simple(self): self.create_member(user=self.user, organization=self.organization, role="owner", teams=[self.team]) self.login_as(self.user) with self.feature("organizations:incidents"): resp = self.get_response( self.organization.slug, self.alert_rule.id, self.trigger.id, self.action.id, type=AlertRuleTriggerAction.get_registered_type( AlertRuleTriggerAction.Type(self.action.type)).slug, target_type=action_target_type_to_string[ AlertRuleTriggerAction.TargetType.TEAM], target_identifier=six.text_type(self.team.id), ) self.action.target_type = AlertRuleTriggerAction.TargetType.TEAM.value self.action.target_identifier = six.text_type(self.team.id) assert resp.data == serialize(self.action) assert resp.data["targetIdentifier"] == six.text_type(self.team.id)
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 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
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" }