Skip to content

feat(np): Adds renderer for MSTeams issue alerts - #112890

Closed
GabeVillalobos wants to merge 1 commit into
masterfrom
gv/add_msteams_issue_renderer
Closed

GabeVillalobos wants to merge 1 commit into
masterfrom
gv/add_msteams_issue_renderer

Conversation

@GabeVillalobos

@GabeVillalobos GabeVillalobos commented Apr 13, 2026 •

Copy link
Copy Markdown
Member

WIP MSTeams issue alert renderer

There are some key things we'll want to fix first though:

  1. The action buttons are different than the current production ones. This basically strips the action dropdowns from the issue notifications, which is less than ideal.
  2. This still queries the event data, which we need to eventually remove.
  3. We need to update the renderer or provider to stitch the integration ID into the notification payload somehow, which is required for webhooks to function correctly.

@github-actions github-actions Bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 13, 2026
Comment thread src/sentry/notifications/platform/msteams/renderers/issue.py
Comment thread src/sentry/notifications/notification_action/utils.py
Comment thread src/sentry/notifications/notification_action/utils.py Outdated
Comment thread src/sentry/notifications/notification_action/utils.py Outdated
Comment thread src/sentry/notifications/platform/msteams/renderers/issue.py

@classmethod
def build_actions(cls, *, group: Group, issue_url: str) -> list[Action]:
return [OpenUrlAction(type=ActionType.OPEN_URL, title="View Issue", url=issue_url)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It might be nice to take the title as a param here; so it's a little easier to modify in the future.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah, good call.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Trying to figure out if this is vestigial? Seems like this is all temporary anyway, and for now there's only one action that's likely to stay "View Issue."

]

@classmethod
def get_renderer(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤔 any thoughts on this being a top level registry? i'm thinking something like: renderer = notification_renderer_registry(Provider.MS_TEAMS, Category.ISSUE)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not opposed to it at all. I'll sync with the team on why we went with this approach to begin with vs rolling another registry.

Comment thread src/sentry/notifications/platform/msteams/renderers/issue.py
class IssueMSTeamsRenderer(NotificationRenderer[MSTeamsRenderable]):
provider_key = NotificationProviderKey.MSTEAMS

@classmethod

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

curious about the class methods on these vs having them be instance methods? is the design pattern here meant to be more generic and allow us to return classes from helper methods and invoke easily? (that's what the get_renderer method seems like at least)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'm not sure what the historical reason for it is, though I do like that it keeps the render classes fully stateless by design.

@getsantry

getsantry Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you add the label WIP, I will leave it alone unless WIP is removed ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry Bot added the Stale label May 6, 2026
@getsantry getsantry Bot closed this May 14, 2026
@getsantry getsantry Bot removed the Stale label May 15, 2026
@getsantry

getsantry Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

This pull request has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you add the label WIP, I will leave it alone unless WIP is removed ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@getsantry getsantry Bot added the Stale label Jun 6, 2026
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Backend Test Failures

Failures on f20bad1 in this run:

tests/sentry/notifications/platform/msteams/renderers/test_issue.py::IssueMSTeamsRendererTest::test_render_produces_card — log
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/notifications/platform/msteams/renderers/test_issue.py:146: in test_render_produces_card
    result = IssueMSTeamsRenderer.render(
src/sentry/notifications/platform/msteams/renderers/issue.py:89: in render
    actions=cls.build_actions(issue_url=issue_url),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E   TypeError: IssueMSTeamsRenderer.build_actions() missing 1 required keyword-only argument: 'group'
tests/sentry/notifications/platform/msteams/renderers/test_issue.py::IssueMSTeamsRendererTest::test_render_with_tags — log
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/notifications/platform/msteams/renderers/test_issue.py:160: in test_render_with_tags
    result = IssueMSTeamsRenderer.render(
src/sentry/notifications/platform/msteams/renderers/issue.py:89: in render
    actions=cls.build_actions(issue_url=issue_url),
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
E   TypeError: IssueMSTeamsRenderer.build_actions() missing 1 required keyword-only argument: 'group'

@hobzcalvin hobzcalvin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

#124860 is the new version of this PR


@classmethod
def build_actions(cls, *, group: Group, issue_url: str) -> list[Action]:
return [OpenUrlAction(type=ActionType.OPEN_URL, title="View Issue", url=issue_url)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Trying to figure out if this is vestigial? Seems like this is all temporary anyway, and for now there's only one action that's likely to stay "View Issue."

hobzcalvin added a commit that referenced this pull request Sep 22, 2026
This was all sort of born out of [this
comment](#112890 (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_renderer` override. This replaces those with a
`NotificationRendererRegistry` keyed by `(provider_key, category)`.

- Renderers register themselves where they are defined, e.g.
`@renderer_registry.register(NotificationProviderKey.SLACK,
NotificationCategory.ISSUE)`.
- `NotificationProvider.get_renderer` is now a single base
implementation: look the pair up, fall back to `default_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_renderer` takes only the notification data now; the category
comes from the notification's registered template rather than the call
site.
- Dropped `provider_key` from the `NotificationRenderer` protocol —
nothing read it.

Optional `renderer_key` on `NotificationProvider` handles the special
`SlackStagingNotificationProvider` case.

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.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
hobzcalvin added a commit that referenced this pull request Sep 23, 2026
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 branch was successfully deployed

1 active deployment
Preview — 38b1e866 Deployed Apr 14, 2026 by vercel[bot]
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 Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants