-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat(ACI): Make ProjectRuleDetailsEndpoint GET method backwards compatible #109387
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
Changes from all commits
d25557d
1b8e68f
75b7dce
c13dc8b
b057ce8
1657dca
7b50880
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,15 @@ | |
|
|
||
| from rest_framework.request import Request | ||
|
|
||
| from sentry import features | ||
| from sentry.api.api_owners import ApiOwner | ||
| from sentry.api.bases import ProjectAlertRulePermission, ProjectEndpoint | ||
| from sentry.api.exceptions import ResourceDoesNotExist | ||
| from sentry.constants import ObjectStatus | ||
| from sentry.incidents.endpoints.serializers.utils import get_object_id_from_fake_id | ||
| from sentry.models.rule import Rule | ||
| from sentry.workflow_engine.models.alertrule_workflow import AlertRuleWorkflow | ||
| from sentry.workflow_engine.models.workflow import Workflow | ||
| from sentry.workflow_engine.utils.legacy_metric_tracking import report_used_legacy_models | ||
|
|
||
|
|
||
|
|
@@ -34,3 +39,42 @@ | |
| raise ResourceDoesNotExist | ||
|
|
||
| return args, kwargs | ||
|
|
||
|
|
||
| class WorkflowEngineRuleEndpoint(RuleEndpoint): | ||
| def convert_args( | ||
| self, request: Request, rule_id: str, *args: Any, **kwargs: Any | ||
| ) -> tuple[Any, Any]: | ||
| args, kwargs = super(RuleEndpoint, self).convert_args(request, *args, **kwargs) | ||
| project = kwargs["project"] | ||
|
|
||
| if not rule_id.isdigit(): | ||
| raise ResourceDoesNotExist | ||
|
|
||
| if features.has("organizations:workflow-engine-rule-serializers", project.organization): | ||
| try: | ||
| arw = AlertRuleWorkflow.objects.get( | ||
| rule_id=rule_id, workflow__organization=project.organization | ||
| ) | ||
| kwargs["rule"] = arw.workflow | ||
|
Check warning on line 59 in src/sentry/api/bases/rule.py
|
||
|
sentry-warden[bot] marked this conversation as resolved.
|
||
| except AlertRuleWorkflow.DoesNotExist: | ||
| # XXX: this means the workflow was single written and has no ARW or related Rule object | ||
| try: | ||
| workflow_id = get_object_id_from_fake_id(int(rule_id)) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We assume we are being passed a "fake id" here because for singly written rules we will not have a rule id, so the rule index endpoint will generate a fake id for a workflow so it can be looked up here. |
||
| kwargs["rule"] = Workflow.objects.get( | ||
| id=workflow_id, | ||
| organization=project.organization, | ||
| status=ObjectStatus.ACTIVE, | ||
| ) | ||
| except (AlertRuleWorkflow.DoesNotExist, Workflow.DoesNotExist): | ||
|
ceorourke marked this conversation as resolved.
|
||
| raise ResourceDoesNotExist | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| return args, kwargs | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Legacy rules return 404 when feature flag enabledHigh Severity When
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's intentional
ceorourke marked this conversation as resolved.
|
||
|
|
||
| report_used_legacy_models() | ||
| try: | ||
| kwargs["rule"] = Rule.objects.get(project=project, id=rule_id) | ||
| except Rule.DoesNotExist: | ||
| raise ResourceDoesNotExist | ||
|
|
||
| return args, kwargs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,19 +9,19 @@ | |
| from rest_framework.request import Request | ||
| from rest_framework.response import Response | ||
|
|
||
| from sentry import analytics, audit_log | ||
| from sentry import analytics, audit_log, features | ||
| from sentry.analytics.events.rule_disable_opt_out import ( | ||
| RuleDisableOptOutEdit, | ||
| RuleDisableOptOutExplicit, | ||
| ) | ||
| from sentry.analytics.events.rule_reenable import RuleReenableEdit | ||
| from sentry.api.api_publish_status import ApiPublishStatus | ||
| from sentry.api.base import region_silo_endpoint | ||
| from sentry.api.bases.rule import RuleEndpoint | ||
| from sentry.api.bases.rule import WorkflowEngineRuleEndpoint | ||
| from sentry.api.endpoints.project_rules import find_duplicate_rule | ||
| from sentry.api.fields.actor import OwnerActorField | ||
| from sentry.api.serializers import serialize | ||
| from sentry.api.serializers.models.rule import RuleSerializer | ||
| from sentry.api.serializers.models.rule import RuleSerializer, WorkflowEngineRuleSerializer | ||
| from sentry.api.serializers.rest_framework.rule import RuleNodeField | ||
| from sentry.api.serializers.rest_framework.rule import RuleSerializer as DrfRuleSerializer | ||
| from sentry.apidocs.constants import ( | ||
|
|
@@ -38,12 +38,14 @@ | |
| from sentry.integrations.jira_server.actions.create_ticket import JiraServerCreateTicketAction | ||
| from sentry.integrations.slack.tasks.find_channel_id_for_rule import find_channel_id_for_rule | ||
| from sentry.integrations.slack.utils.rule_status import RedisRuleStatus | ||
| from sentry.models.project import Project | ||
| from sentry.models.rule import NeglectedRule, Rule, RuleActivity, RuleActivityType | ||
| from sentry.projects.project_rules.updater import ProjectRuleUpdater | ||
| from sentry.rules.actions import trigger_sentry_app_action_creators_for_issues | ||
| from sentry.sentry_apps.utils.errors import SentryAppBaseError | ||
| from sentry.signals import alert_rule_edited | ||
| from sentry.types.actor import Actor | ||
| from sentry.workflow_engine.models.workflow import Workflow | ||
| from sentry.workflow_engine.utils.legacy_metric_tracking import ( | ||
| report_used_legacy_models, | ||
| track_alert_endpoint_execution, | ||
|
|
@@ -99,7 +101,7 @@ class ProjectRuleDetailsPutSerializer(serializers.Serializer): | |
|
|
||
| @extend_schema(tags=["Alerts"]) | ||
| @region_silo_endpoint | ||
| class ProjectRuleDetailsEndpoint(RuleEndpoint): | ||
| class ProjectRuleDetailsEndpoint(WorkflowEngineRuleEndpoint): | ||
| publish_status = { | ||
| "DELETE": ApiPublishStatus.PUBLIC, | ||
| "GET": ApiPublishStatus.PUBLIC, | ||
|
|
@@ -122,7 +124,7 @@ class ProjectRuleDetailsEndpoint(RuleEndpoint): | |
| examples=IssueAlertExamples.GET_PROJECT_RULE, | ||
| ) | ||
| @track_alert_endpoint_execution("GET", "sentry-api-0-project-rule-details") | ||
| def get(self, request: Request, project, rule) -> Response: | ||
| def get(self, request: Request, project: Project, rule: Rule | Workflow) -> Response: | ||
| """ | ||
| ## Deprecated | ||
| 🚧 Use [Fetch an Alert](/api/monitors/fetch-an-alert) instead. | ||
|
|
@@ -135,13 +137,23 @@ def get(self, request: Request, project, rule) -> Response: | |
| - Filters - help control noise by triggering an alert only if the issue matches the specified criteria. | ||
| - Actions - specify what should happen when the trigger conditions are met and the filters match. | ||
| """ | ||
| # Serialize Rule object | ||
| rule_serializer = RuleSerializer( | ||
| expand=request.GET.getlist("expand", []), | ||
| prepare_component_fields=True, | ||
| project_slug=project.slug, | ||
| ) | ||
| serialized_rule = serialize(rule, request.user, rule_serializer) | ||
| if features.has("organizations:workflow-engine-rule-serializers", project.organization): | ||
| workflow_engine_rule_serializer = WorkflowEngineRuleSerializer( | ||
| expand=request.GET.getlist("expand", []), | ||
| prepare_component_fields=True, | ||
| project_slug=project.slug, | ||
| ) | ||
| # XXX: Note 'rule' here is actually a workflow object | ||
| serialized_rule = serialize(rule, request.user, workflow_engine_rule_serializer) | ||
| else: | ||
| # Serialize Rule object | ||
| rule_serializer = RuleSerializer( | ||
| expand=request.GET.getlist("expand", []), | ||
| prepare_component_fields=True, | ||
| project_slug=project.slug, | ||
| ) | ||
| serialized_rule = serialize(rule, request.user, rule_serializer) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GET uses wrong serializer when legacy Rule returnedHigh Severity The GET handler selects the serializer using only the feature flag. If
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
idk why you'd think that would happen |
||
|
|
||
|
cursor[bot] marked this conversation as resolved.
|
||
| # Prepare Rule Actions that are SentryApp components using the meta fields | ||
| for action in serialized_rule.get("actions", []): | ||
| # TODO(nisanthan): This is a temporary fix. We need to save both the label and value of | ||
|
|
@@ -174,7 +186,7 @@ def get(self, request: Request, project, rule) -> Response: | |
| examples=IssueAlertExamples.UPDATE_PROJECT_RULE, | ||
| ) | ||
| @track_alert_endpoint_execution("PUT", "sentry-api-0-project-rule-details") | ||
| def put(self, request: Request, project, rule) -> Response: | ||
| def put(self, request: Request, project: Project, rule: Rule | Workflow) -> Response: | ||
| """ | ||
| ## Deprecated | ||
| 🚧 Use [Update an Alert by ID](/api/monitors/update-an-alert-by-id) instead. | ||
|
|
@@ -188,6 +200,16 @@ def put(self, request: Request, project, rule) -> Response: | |
| - Filters - help control noise by triggering an alert only if the issue matches the specified criteria. | ||
| - Actions - specify what should happen when the trigger conditions are met and the filters match. | ||
| """ | ||
| if isinstance(rule, Workflow): | ||
| return Response( | ||
| { | ||
| "rule": [ | ||
| "Passing a workflow through this endpoint is not yet supported", | ||
| ] | ||
| }, | ||
| status=status.HTTP_400_BAD_REQUEST, | ||
| ) | ||
|
|
||
| rule_data_before = dict(rule.data) | ||
| if rule.environment_id: | ||
| rule_data_before["environment_id"] = rule.environment_id | ||
|
|
@@ -383,7 +405,7 @@ def put(self, request: Request, project, rule) -> Response: | |
| }, | ||
| ) | ||
| @track_alert_endpoint_execution("DELETE", "sentry-api-0-project-rule-details") | ||
| def delete(self, request: Request, project, rule) -> Response: | ||
| def delete(self, request: Request, project: Project, rule: Rule | Workflow) -> Response: | ||
| """ | ||
| ## Deprecated | ||
| 🚧 Use [Delete an Alert](/api/monitors/delete-an-alert) instead. | ||
|
|
@@ -396,6 +418,16 @@ def delete(self, request: Request, project, rule) -> Response: | |
| - Filters: help control noise by triggering an alert only if the issue matches the specified criteria. | ||
| - Actions: specify what should happen when the trigger conditions are met and the filters match. | ||
| """ | ||
| if isinstance(rule, Workflow): | ||
| return Response( | ||
| { | ||
| "rule": [ | ||
| "Passing a workflow through this endpoint is not yet supported", | ||
| ] | ||
| }, | ||
| status=status.HTTP_400_BAD_REQUEST, | ||
| ) | ||
|
|
||
| report_used_legacy_models() | ||
| with transaction.atomic(router.db_for_write(Rule)): | ||
| rule.update(status=ObjectStatus.PENDING_DELETION) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -449,6 +449,7 @@ def _fetch_workflow_last_triggered(self, item_list: Sequence[Workflow]) -> dict[ | |
| return {wf_id: date for wf_id, date in results.items() if date is not None} | ||
|
|
||
| def get_attrs(self, item_list: Sequence[Workflow], user, **kwargs): | ||
| from sentry.incidents.endpoints.serializers.utils import get_fake_id_from_object_id | ||
| from sentry.notifications.notification_action.registry import issue_alert_handler_registry | ||
|
|
||
| # Bulk fetch users that created workflows | ||
|
|
@@ -477,7 +478,9 @@ def get_attrs(self, item_list: Sequence[Workflow], user, **kwargs): | |
|
|
||
| result[workflow]["environment"] = workflow.environment | ||
| result[workflow]["projects"] = list(workflow_to_projects[workflow]) | ||
| result[workflow]["rule_id"] = workflow_rule_ids[workflow.id] | ||
| result[workflow]["rule_id"] = workflow_rule_ids.get( | ||
| workflow.id, get_fake_id_from_object_id(workflow.id) | ||
| ) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IndexError when workflow has no WorkflowDataConditionGroupMedium Severity
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it'll always have one |
||
|
|
||
| result[workflow]["action_match"] = ( | ||
| workflow.when_condition_group.logic_type if workflow.when_condition_group else None | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The code accesses Suggested FixAdd a conditional check to ensure Prompt for AI Agent |
||
|
|
@@ -521,7 +524,6 @@ def get_attrs(self, item_list: Sequence[Workflow], user, **kwargs): | |
| conditions, filters = self._generate_rule_conditions_filters( | ||
| workflow, result[workflow]["projects"][0], workflow_dcg | ||
| ) | ||
|
|
||
| result[workflow]["conditions"] = conditions | ||
| result[workflow]["filters"] = filters | ||
|
|
||
|
|
||


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 had to make a new class here because
RuleEndpointencompassesProjectRuleGroupHistoryIndexEndpointandProjectRuleStatsIndexEndpointas well which we don't want to affect.