Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion static/app/router/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {ProvideAriaRouter} from 'sentry/utils/provideAriaRouter';
import {translateSentryRoute} from 'sentry/utils/reactRouter6Compat/router';
import withDomainRedirect from 'sentry/utils/withDomainRedirect';
import withDomainRequired from 'sentry/utils/withDomainRequired';
import {
WorkflowEngineRedirectToAutomationDetails,
WorkflowEngineRedirectToAutomationEdit,
} from 'sentry/views/alerts/workflowEngineRedirects';
import App from 'sentry/views/app';
import {AppBodyContent} from 'sentry/views/app/appBodyContent';
import AuthLayout from 'sentry/views/auth/layout';
Expand Down Expand Up @@ -1487,13 +1491,22 @@ function buildRoutes(): RouteObject[] {
},
{
path: ':ruleId/',
component: make(() => import('sentry/views/alerts/edit')),
component: WorkflowEngineRedirectToAutomationEdit,
deprecatedRouteProps: true,
children: [
{
index: true,
component: make(() => import('sentry/views/alerts/edit')),
deprecatedRouteProps: true,
},
],
},
],
},
{
path: ':projectId/:ruleId/details/',
component: WorkflowEngineRedirectToAutomationDetails,
deprecatedRouteProps: true,
children: [
{
index: true,
Expand Down
109 changes: 109 additions & 0 deletions static/app/views/alerts/workflowEngineRedirects.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {cloneElement, isValidElement} from 'react';

import LoadingIndicator from 'sentry/components/loadingIndicator';
import Redirect from 'sentry/components/redirect';
import {useApiQuery} from 'sentry/utils/queryClient';
import useOrganization from 'sentry/utils/useOrganization';
import {useParams} from 'sentry/utils/useParams';
import {useUser} from 'sentry/utils/useUser';
import {
makeAutomationDetailsPathname,
makeAutomationEditPathname,
} from 'sentry/views/automations/pathnames';

interface AlertRuleWorkflow {
alertRuleId: string | null;
ruleId: string | null;
workflowId: string;
}

/**
* Base component for workflow engine redirects that require fetching
* workflow data from a rule before redirecting.
*/
function WorkflowEngineRedirectWithData({
children,
makeRedirectPath,
...props
}: {
children: React.ReactNode;
makeRedirectPath: (workflowId: string, orgSlug: string) => string;
}) {
const user = useUser();
const organization = useOrganization();
const params = useParams<{projectId: string; ruleId: string}>();
const {ruleId} = params;

const shouldRedirect =
!user.isStaff && organization.features.includes('workflow-engine-ui');

const {data: alertRuleWorkflow, isPending} = useApiQuery<AlertRuleWorkflow>(
[
`/organizations/${organization.slug}/alert-rule-workflow/`,
{query: {rule_id: ruleId}},
],
{
staleTime: 0,
enabled: shouldRedirect,
retry: false,
}
);

if (shouldRedirect) {
if (isPending) {
return <LoadingIndicator />;
}
if (alertRuleWorkflow) {
return (
<Redirect
to={makeRedirectPath(alertRuleWorkflow.workflowId, organization.slug)}
/>
);
}
}

// Pass through all props to children
if (isValidElement(children)) {
return cloneElement(children, props);
}

return children;
Copy link
Contributor

Choose a reason for hiding this comment

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

Bug: Child routes not rendered without Outlet component

The WorkflowEngineRedirectWithData component attempts to render child routes by cloning the children prop, but in React Router v6, nested routes require using the <Outlet /> component instead. The current code checks isValidElement(children) which will be false for route configurations, causing it to return the raw children array which won't render anything. When redirect conditions aren't met (e.g., feature flag disabled or API returns null), the child route components won't be displayed, breaking the normal alert rule pages.

Fix in Cursor Fix in Web

}

// Data-dependent redirects

export function WorkflowEngineRedirectToAutomationDetails({
children,
...props
}: {
children: React.ReactNode;
}) {
return (
<WorkflowEngineRedirectWithData
makeRedirectPath={(workflowId, orgSlug) =>
makeAutomationDetailsPathname(orgSlug, workflowId)
}
{...props}
>
{children}
</WorkflowEngineRedirectWithData>
);
}

export function WorkflowEngineRedirectToAutomationEdit({
children,
...props
}: {
children: React.ReactNode;
}) {
return (
<WorkflowEngineRedirectWithData
makeRedirectPath={(workflowId, orgSlug) =>
makeAutomationEditPathname(orgSlug, workflowId)
}
{...props}
>
{children}
</WorkflowEngineRedirectWithData>
);
}
4 changes: 3 additions & 1 deletion tests/acceptance/test_organization_alert_rule_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
from sentry.models.rule import Rule
from sentry.models.rulefirehistory import RuleFireHistory
from sentry.testutils.cases import AcceptanceTestCase, SnubaTestCase
from sentry.testutils.helpers import with_feature
from sentry.testutils.silo import no_silo_test


@no_silo_test
@with_feature({"organizations:workflow-engine-ui": False})
class OrganizationAlertRuleDetailsTest(AcceptanceTestCase, SnubaTestCase):
def setUp(self) -> None:
super().setUp()
self.login_as(self.user)
self.project = self.create_project(fire_project_created=True)
self.rule = Rule.objects.get(project=self.project)
self.path = f"/organizations/{self.organization.slug}/alerts/rules/{self.project.slug}/{self.rule.id}/details/"
self.path = f"/organizations/{self.organization.slug}/issues/alerts/rules/{self.project.slug}/{self.rule.id}/details/"

def test_empty_alert_rule_details(self) -> None:
self.browser.get(self.path)
Expand Down
Loading