-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨ poc(nowa): poc for issue alert migration #81288
base: master
Are you sure you want to change the base?
Conversation
@@ -21,8 +39,36 @@ class Action(DefaultFieldsModel): | |||
|
|||
# TODO (@saponifi3d): Don't hardcode these values, and these are incomplete values | |||
class Type(models.TextChoices): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can change. just have this here so i can use this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 yeah - let's update this a bit, happy to chat through ideas!
❌ 1 Tests Failed:
View the top 1 failed tests by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
Action.Type.NOTIFICATION_EMAIL, | ||
] | ||
) | ||
def notification_action_handler(action: Action, group_event: GroupEvent): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the entrypoint of NOWA, it will be invoked when someone calls trigger_action
with one of the registered action types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i am having trouble with this registry registering in 1 of my tests, if i import it it claims i registered this function twice. if i don't, it says this isn't registered 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think it might be the way you're using the registry. i'd just do something like, @action_handler_registry.register(Action.Type.NOTIFICATION)
to encapsulate any notification.
you can also stack the decorators if we keep many kinds of notifications:
@action_handler_registry.register(Action.Type.NOTIFICATION_SLACK)
@action_handler_registry.register(Action.Type.NOTIFICATION_DISCORD)
@action_handler_registry.register(Action.Type.NOTIFICATION_MSTEAMS)
def notification_action_handler(action: Action, group_event: GroupEvent):
pass
raise ValueError(f"No handler found for action type: {action.type}") | ||
|
||
|
||
def trigger_action(action: Action, group_event: GroupEvent): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other parts of ACI can call this function which will trigger nowa if the action.type is correct
@@ -0,0 +1,137 @@ | |||
import uuid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meat of nowa
def register_action_handler(action_types: list[Action.Type]): | ||
""" | ||
Decorator to register a handler for specific action types. | ||
|
||
Usage: | ||
@register_action_handler([Action.Type.NOTIFICATION_SLACK, Action.Type.NOTIFICATION_DISCORD]) | ||
def notification_action_handler(action: Action, group_event: GroupEvent): | ||
... | ||
""" | ||
|
||
def decorator(handler_class: ActionHandler): | ||
for action_type in action_types: | ||
action_handler_registry.register(action_type.value)(handler_class) | ||
return handler_class | ||
|
||
return decorator | ||
|
||
|
||
def get_handler(action: Action) -> ActionHandler: | ||
"""Get the handler instance for a given action.""" | ||
try: | ||
handler_class = action_handler_registry.get(action.type.value) | ||
return handler_class() | ||
except NoRegistrationExistsError: | ||
raise ValueError(f"No handler found for action type: {action.type}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you shouldn't nee to create these, you can just use the action_handler_registry
directly.
@action_handler_registry.register(Action.Type)
def handler(action: Action, group_event: GroupEvent):
pass
and
action_handler_register.get(Action.Type)
(i can also create these as they're part of milestone 1 -- i'm about to need this registry on my branch, i'll probably just copy pasta bits of this 🎉)
from sentry.workflow_engine.models.detector import Detector | ||
|
||
|
||
def build_rule_data_blob(action: Action) -> list[dict[str, Any]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this dict
shaped like anything in particular? it seems like we always have an id, uuid, integration_id_key (based on the action type), etc -- can we make a type that encapsulates the expected shape?
src/sentry/workflow_engine/actions/notification_action/migration_utils.py
Outdated
Show resolved
Hide resolved
src/sentry/workflow_engine/actions/notification_action/mappings.py
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,137 @@ | |||
import uuid | |||
from typing import Any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After moving around the other files, we could then update this to be notification_action.py
-- that might be nice to keep it actions/<action_implementation>.py
- and keeping it flat is one of the big wins of using a registry for just the handler method.
src/sentry/workflow_engine/actions/notification_action/migration_utils.py
Outdated
Show resolved
Hide resolved
} | ||
|
||
|
||
def build_notification_actions_from_rule_data(actions: list[dict[str, Any]]) -> list[Action]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how can we improve the typing here? Is there a specific format for each action?
:param group_event: GroupEvent model instance | ||
""" | ||
|
||
rule, rule_fire_history, notification_uuid = form_issue_alert_models( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for production code, let's add a span
to this method so we can see how long we're spending creating legacy models per notification.
:param group_event: GroupEvent model instance | ||
""" | ||
|
||
rule, rule_fire_history, notification_uuid = form_issue_alert_models( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, should we add a check to see if we need to create a new rule vs fetching an existing rule from the db?
) | ||
|
||
# TODO(iamrajjoshi): Add a check to see if the rule has only one action | ||
assert len(rule.data.get("actions", [])) == 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a little nervous that this will really increase the number of rules we have in the db; it seems like for each notification we're sending; we'll be creating a new rule (so if the workflow has 5 actions, we'll create 5 rules with 1 action each). It also seems like we're creating it every time, but not removing it and not reusing it.
} | ||
|
||
|
||
class RuleDataBlob: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
working through this at the moment, need to figure out how we can handle dynamic fields
""" | ||
IntegrationIdKey is the key used to identify the integration in the rule data. | ||
""" | ||
IntegrationIdKey = Union[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used atm
35b8b7e
to
488376e
Compare
488376e
to
b186dea
Compare
This issue 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 remove the label "A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀 |
this is a poc to see how issue alerts notifications will work using NOWA in ACI.
i am planning to build this out so we can fire notifications, but currently i have the migration path from
Rule
->Action
and the first part of the NOWA logic, which translates anAction
to legacy models (Rule
andRuleFireHistory
)edit: added the
invoke_nowa
function and was able to successfully send a notification using the new models.currently i convert a Rule to Action and back to a Rule, and send a notification using the new rule.