Repository navigation
ref(np): Dispatch provider renderers through a registry - #124857
Conversation
6f19cf1 to
aa068fe
Compare
ada4902 to
fa6b45c
Compare
1a74a96 to
d964d6d
Compare
| # Imported here since the registry imports this module to type its registrations. | ||
| from sentry.notifications.platform.registry import renderer_registry | ||
|
|
||
| renderer = renderer_registry.get( | ||
| provider_key=cls.renderer_key or cls.key, source=data.source | ||
| ) | ||
| return renderer or cls.default_renderer |
There was a problem hiding this comment.
Is this just used as a fallback? Would it be worth just having the notification service and delivery tasks access the registry directly? I could see a case for either approach tbh.
There was a problem hiding this comment.
I thought about calling the registry directly, but this couples the cls.renderer_key or cls.key and cls.default_renderer in here nicely, so the caller doesn't need to worry about those details.
GabeVillalobos
left a comment
There was a problem hiding this comment.
Changes look great! This removes a ton of boilerplate and makes it more obvious how these renderers map to their notifications. Nice work
| renderer = DiscordNotificationProvider.get_renderer( | ||
| data=data, category=NotificationCategory.DEBUG | ||
| ) | ||
| renderer = DiscordNotificationProvider.get_renderer(data=data) |
There was a problem hiding this comment.
These tests seem a little pointless now, can we remove these in a follow-up PR please?
There was a problem hiding this comment.
the get_renderer hop isn't needed, but the rest is...I think I can just remove this go-around?
963441d to
f42dbb7
Compare
f558636 to
75c7b35
Compare
75c7b35 to
4561aa6
Compare
Replaces the per-provider `get_renderer` overrides with a `NotificationRendererRegistry` keyed by provider and notification source. Renderers register themselves where they are defined, and `NotificationProvider.get_renderer` resolves the renderer from the notification's source, falling back to the provider's default renderer. Keying on source rather than category lets the Seer renderers be registered directly, replacing the nested source check the Slack provider needed to separate `SeerAgentWriteApprovalSlackRenderer` from `SeerSlackRenderer`. `SlackStagingNotificationProvider` resolves against the Slack key, since it sends Slack payloads under its own provider key. The registry imports renderer modules on first lookup rather than from `AppConfig.ready()`: renderers reach into integration packages whose __init__ imports `sentry.integrations.jira.client`, which reads `system.url-prefix` at module scope before the options cache exists. Also drops the unused `provider_key` attribute from the `NotificationRenderer` protocol. Co-authored-by: Cursor <cursoragent@cursor.com>
Matches how other registration decorators in the codebase accept a set of values, e.g. AlertRuleTriggerAction.register_type's supported_target_types. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Importing the jira integration read `system.url-prefix` at module scope, which raises when the renderer modules are imported from `AppConfig.ready()`, before the options store cache is bound. Co-authored-by: Cursor <cursoragent@cursor.com>
This reverts commit 75c7b35.
The renderer modules are imported from `AppConfig.ready()`, which Django runs during `django.setup()` — before `initialize_app` calls `bind_cache_to_option_store()`. Importing `sentry.integrations.discord` or `sentry.integrations.slack` at module scope executes those packages' `__init__`, which reaches `sentry.notifications.notification_action` and from there the Jira action validator, whose module scope reads `system.url-prefix`. That read asserts while the options store cache is unbound. Deferring the imports into `render()` matches what the Discord and Slack providers and `IssueSlackRenderer` already do, and keeps the registrations eager at `ready()`. Co-authored-by: Cursor <cursoragent@cursor.com>
4561aa6 to
6c595a5
Compare
Stacked on #124857 (`gp/add_renderer_registry`). Supersedes #112890, which could not be reopened after the branch was rebased. ### Summary Adds `IssueMSTeamsRenderer`, so issue alerts sent through the notification platform produce the same Adaptive Card the legacy `MSTeamsIssueMessageBuilder` path produces today. The renderer registers itself for `(MSTEAMS, ISSUE)` with the registry from the parent PR rather than adding a `get_renderer` override to the provider. Original work by @GabeVillalobos; rebased onto current master and adapted to the registry. Actions are limited to a single "View Issue" link. The legacy card also offers resolve/archive/assign, which need the `RpcIntegration` for action webhook routing; that is coming in #125160 --------- Co-authored-by: Gabe Villalobos <gabriel.villalobos@sentry.io> Co-authored-by: Cursor <cursoragent@cursor.com>
This was all sort of born out of this comment on the original attempt to add MSTeams issue alerts months ago.
Each provider was hand-rolling the same category → renderer dispatch table inside its own
get_rendereroverride. This replaces those with aNotificationRendererRegistrykeyed by(provider_key, category).@renderer_registry.register(NotificationProviderKey.SLACK, NotificationCategory.ISSUE).NotificationProvider.get_rendereris now a single base implementation: look the pair up, fall back todefault_renderer. The Slack, Discord, and MS Teams overrides are gone, and with them the function-local renderer imports they needed to dodge the renderer → provider circular import.get_renderertakes only the notification data now; the category comes from the notification's registered template rather than the call site.provider_keyfrom theNotificationRendererprotocol — nothing read it.Optional
renderer_keyonNotificationProviderhandles the specialSlackStagingNotificationProvidercase.All renderers are now imported in
src/sentry/notifications/platform/registry.py. I looked into something automatic, but it seemed dicey. At least there's one place where all these imports live now, and other than that, renderers register themselves.