Skip to content

Conversation

@ceorourke
Copy link
Member

@ceorourke ceorourke commented Nov 20, 2025

Fixes adding sentry app actions to a workflow and requires settings to be passed for a sentry app action if it has a schema. If it does not have a schema, settings does not need to be passed. Add tests for adding and updating sentry app actions.

Fixes https://sentry.sentry.io/issues/6980690322

Do not merge until we can run the migration to move sentry_app_installation_uuid -> sentry_app_id (or support both).

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Nov 20, 2025
@codecov
Copy link

codecov bot commented Nov 20, 2025

❌ 7 Tests Failed:

Tests completed Failed Passed Skipped
29979 7 29972 245
View the top 3 failed test(s) by shortest run time
tests.sentry.workflow_engine.endpoints.test_organization_workflow_details.OrganizationUpdateWorkflowTest::test_update_add_sentry_app_action
Stack Traces | 3.55s run time
#x1B[1m#x1B[.../workflow_engine/endpoints/test_organization_workflow_details.py#x1B[0m:131: in test_update_add_sentry_app_action
    response = self.get_success_response(
#x1B[1m#x1B[.../sentry/testutils/cases.py#x1B[0m:623: in get_success_response
    assert_status_code(response, status.HTTP_200_OK)
#x1B[1m#x1B[.../sentry/testutils/asserts.py#x1B[0m:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
#x1B[1m#x1B[31mE   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')#x1B[0m
#x1B[1m#x1B[31mE   assert 500 < 201#x1B[0m
#x1B[1m#x1B[31mE    +  where 500 = <Response status_code=500, "application/json">.status_code#x1B[0m
tests.sentry.workflow_engine.endpoints.validators.actions.test_sentry_app.TestSentryAppActionValidator::test_validate__rpc_failure
Stack Traces | 4.24s run time
#x1B[1m#x1B[.../validators/actions/test_sentry_app.py#x1B[0m:72: in test_validate__rpc_failure
    result = validator.is_valid()
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:225: in is_valid
    self._validated_data = self.run_validation(self.initial_data)
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:447: in run_validation
    value = self.validate(value)
#x1B[1m#x1B[.../validators/base/action.py#x1B[0m:96: in validate
    return handler(attrs, organization).clean_data()
#x1B[1m#x1B[.../notifications/notification_action/action_validation.py#x1B[0m:215: in clean_data
    "sentryAppInstallationUuid": installation.uuid,
#x1B[1m#x1B[31mE   AttributeError: 'NoneType' object has no attribute 'uuid'#x1B[0m
tests.sentry.workflow_engine.endpoints.test_organization_workflow_index.OrganizationWorkflowCreateTest::test_create_workflow_with_sentry_app_action
Stack Traces | 4.92s run time
#x1B[1m#x1B[.../workflow_engine/endpoints/test_organization_workflow_index.py#x1B[0m:545: in test_create_workflow_with_sentry_app_action
    response = self.get_success_response(
#x1B[1m#x1B[.../sentry/testutils/cases.py#x1B[0m:628: in get_success_response
    assert_status_code(response, 200, 300)
#x1B[1m#x1B[.../sentry/testutils/asserts.py#x1B[0m:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
#x1B[1m#x1B[31mE   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')#x1B[0m
#x1B[1m#x1B[31mE   assert 500 < 300#x1B[0m
#x1B[1m#x1B[31mE    +  where 500 = <Response status_code=500, "application/json">.status_code#x1B[0m
tests.sentry.workflow_engine.endpoints.test_organization_workflow_index.OrganizationWorkflowCreateTest::test_create_sentry_app_action_missing_settings
Stack Traces | 5.08s run time
#x1B[1m#x1B[.../workflow_engine/endpoints/test_organization_workflow_index.py#x1B[0m:604: in test_create_sentry_app_action_missing_settings
    assert response.status_code == 400
#x1B[1m#x1B[31mE   assert 500 == 400#x1B[0m
#x1B[1m#x1B[31mE    +  where 500 = <Response status_code=500, "application/json">.status_code#x1B[0m
tests.sentry.workflow_engine.endpoints.validators.actions.test_sentry_app.TestSentryAppActionValidator::test_validate_settings_action_trigger
Stack Traces | 6.15s run time
#x1B[1m#x1B[.../validators/actions/test_sentry_app.py#x1B[0m:128: in test_validate_settings_action_trigger
    result = validator.is_valid()
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:225: in is_valid
    self._validated_data = self.run_validation(self.initial_data)
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:447: in run_validation
    value = self.validate(value)
#x1B[1m#x1B[.../validators/base/action.py#x1B[0m:96: in validate
    return handler(attrs, organization).clean_data()
#x1B[1m#x1B[.../notifications/notification_action/action_validation.py#x1B[0m:210: in clean_data
    id=int(self.validated_data["config"]["target_identifier"])
#x1B[1m#x1B[31mE   ValueError: invalid literal for int() with base 10: '9539e596-ff64-4be6-8f34-035155050af8'#x1B[0m
tests.sentry.workflow_engine.endpoints.validators.actions.test_sentry_app.TestSentryAppActionValidator::test_validate
Stack Traces | 6.2s run time
#x1B[1m#x1B[.../validators/actions/test_sentry_app.py#x1B[0m:51: in test_validate
    result = validator.is_valid()
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:225: in is_valid
    self._validated_data = self.run_validation(self.initial_data)
#x1B[1m#x1B[31m.venv/lib/python3.13....../site-packages/rest_framework/serializers.py#x1B[0m:447: in run_validation
    value = self.validate(value)
#x1B[1m#x1B[.../validators/base/action.py#x1B[0m:96: in validate
    return handler(attrs, organization).clean_data()
#x1B[1m#x1B[.../notifications/notification_action/action_validation.py#x1B[0m:215: in clean_data
    "sentryAppInstallationUuid": installation.uuid,
#x1B[1m#x1B[31mE   AttributeError: 'NoneType' object has no attribute 'uuid'#x1B[0m
tests.sentry.workflow_engine.endpoints.test_organization_workflow_index.OrganizationWorkflowCreateTest::test_create_sentry_app_action_no_settings
Stack Traces | 6.97s run time
#x1B[1m#x1B[.../workflow_engine/endpoints/test_organization_workflow_index.py#x1B[0m:653: in test_create_sentry_app_action_no_settings
    response = self.get_success_response(
#x1B[1m#x1B[.../sentry/testutils/cases.py#x1B[0m:628: in get_success_response
    assert_status_code(response, 200, 300)
#x1B[1m#x1B[.../sentry/testutils/asserts.py#x1B[0m:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
#x1B[1m#x1B[31mE   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')#x1B[0m
#x1B[1m#x1B[31mE   assert 500 < 300#x1B[0m
#x1B[1m#x1B[31mE    +  where 500 = <Response status_code=500, "application/json">.status_code#x1B[0m

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

Comment on lines 160 to 162
"sentryAppIdentifier": SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID,
"targetIdentifier": self.sentry_app_installation.uuid,
"targetType": ActionType.SENTRY_APP,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ameliahsu I was only able to get this test to pass if I used this data which differs from what was passed in https://sentry.sentry.io/issues/6980690322 which was

{
    "sentry_app_identifier":"'sentry_app_id'",
    "target_identifier":"'72013'",
    "target_type":"'sentry_app'"
}

Do you know if the data in the Sentry issue is what's expected to be passed all the time or only sometimes? I'm not sure when we pass one versus the other, but if we set sentryAppIdentifier to "sentry_app" instead of "sentry_app_installation" (SentryAppIdentifier.SENTRY_APP_INSTALLATION_UUID) we go down a slightly different code path and set sentryAppInstallationUuid to None (code here) which leads to a SentryAppInstallation.DoesNotExist error (here).

I truly don't know if this is a bug or how it's intended to work, but it doesn't currently work if you do not pass the installation data. I did verify that even an internal unpublished sentry app has an installation object, so maybe it's intended for us to always pass that data?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GabeVillalobos might be able to help here, i believe some existing app actions use the sentry app UUID instead of the sentry app ID, which is why we have to specify sentry_app_identifier? but we would like for all newly created actions going forward to use sentry app ID. not sure if that's why we throw an error tho 😅

@ceorourke
Copy link
Member Author

ceorourke commented Nov 21, 2025

I'm finding that I'm between a rock and a hard place because the old alert rule tests are failing when we dual write to ACI models and don't pass settings, but in the old world that can be valid if it's for a webhook. I might hard disable dual write on the old tests as a workaround - do we know if we run into this problem today when dual writing a rule with a sentry app webhook action (aka a sentry app that doesn't have options that would go into settings)?

Update - I ran into this test with no settings failing and I don't know if this is supposed to be valid or not.

"properties": {
"settings": {"type": ["array", "object"]},
},
"required": ["settings"],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I no longer feel confident this should be required - we have actions in the db without data. Are those invalid? If so what should we do about them?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants