diff --git a/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx b/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx index d815292bc09951..48d49cf8e59de5 100644 --- a/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx +++ b/static/app/views/issueDetails/groupEventDetails/groupEventDetailsContent.tsx @@ -71,14 +71,13 @@ import {getReplayIdFromEvent} from 'sentry/utils/replays/getReplayIdFromEvent'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import {useParams} from 'sentry/utils/useParams'; +import {MetricIssuesSection} from 'sentry/views/issueDetails/metricIssues/metricIssuesSection'; import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {EventDetails} from 'sentry/views/issueDetails/streamline/eventDetails'; import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; import {TraceDataSection} from 'sentry/views/issueDetails/traceDataSection'; import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils'; -import MetricIssuesSection from '../metricIssuesSection'; - const LLMMonitoringSection = lazy( () => import('sentry/components/events/interfaces/llm-monitoring/llmMonitoringSection') ); @@ -230,7 +229,6 @@ export function EventDetailsContent({ )} diff --git a/static/app/views/issueDetails/streamline/metricIssueChart.tsx b/static/app/views/issueDetails/metricIssues/metricIssueChart.tsx similarity index 64% rename from static/app/views/issueDetails/streamline/metricIssueChart.tsx rename to static/app/views/issueDetails/metricIssues/metricIssueChart.tsx index 086e4460490c9c..51f53b6a596211 100644 --- a/static/app/views/issueDetails/streamline/metricIssueChart.tsx +++ b/static/app/views/issueDetails/metricIssues/metricIssueChart.tsx @@ -1,46 +1,36 @@ -import {Fragment, lazy, useMemo} from 'react'; +import {lazy} from 'react'; import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; -import moment from 'moment-timezone'; -import {DateTime} from 'sentry/components/dateTime'; import LazyLoad from 'sentry/components/lazyLoad'; -import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; -import type {Event} from 'sentry/types/event'; import type {Group} from 'sentry/types/group'; import type {Project} from 'sentry/types/project'; import useApi from 'sentry/utils/useApi'; import useOrganization from 'sentry/utils/useOrganization'; -import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants'; import { getFilter, getPeriodInterval, } from 'sentry/views/alerts/rules/metric/details/utils'; -import {Dataset, TimePeriod} from 'sentry/views/alerts/rules/metric/types'; +import {Dataset} from 'sentry/views/alerts/rules/metric/types'; import {extractEventTypeFilterFromRule} from 'sentry/views/alerts/rules/metric/utils/getEventTypeFilter'; import {isCrashFreeAlert} from 'sentry/views/alerts/rules/metric/utils/isCrashFreeAlert'; import {useMetricRule} from 'sentry/views/alerts/rules/metric/utils/useMetricRule'; +import { + useMetricIssueAlertId, + useMetricTimePeriod, +} from 'sentry/views/issueDetails/metricIssues/utils'; const MetricChart = lazy( () => import('sentry/views/alerts/rules/metric/details/metricChart') ); -export function MetricIssueChart({ - event, - group, - project, -}: { - event: Event | undefined; - group: Group; - project: Project; -}) { +export function MetricIssuesChart({group, project}: {group: Group; project: Project}) { const theme = useTheme(); const api = useApi(); const organization = useOrganization(); - const ruleId = event?.contexts?.metric_alert?.alert_rule_id; - + const ruleId = useMetricIssueAlertId({groupId: group.id}); const {data: rule} = useMetricRule( { orgSlug: organization.slug, @@ -55,33 +45,7 @@ export function MetricIssueChart({ enabled: !!ruleId, } ); - - const openPeriod = group.openPeriods?.[0]; - const timePeriod = useMemo((): TimePeriodType | null => { - if (!openPeriod) { - return null; - } - const start = openPeriod.start; - let end = openPeriod.end; - if (!end) { - end = new Date().toISOString(); - } - return { - start, - end, - period: TimePeriod.SEVEN_DAYS, - usingPeriod: false, - label: t('Custom time'), - display: ( - - - {' — '} - - - ), - custom: true, - }; - }, [openPeriod]); + const timePeriod = useMetricTimePeriod({openPeriod: group.openPeriods?.[0]}); if (!rule || !timePeriod) { return null; diff --git a/static/app/views/issueDetails/metricIssuesSection.tsx b/static/app/views/issueDetails/metricIssues/metricIssuesSection.tsx similarity index 76% rename from static/app/views/issueDetails/metricIssuesSection.tsx rename to static/app/views/issueDetails/metricIssues/metricIssuesSection.tsx index cddb20d075f1c3..da8e42d723609f 100644 --- a/static/app/views/issueDetails/metricIssuesSection.tsx +++ b/static/app/views/issueDetails/metricIssues/metricIssuesSection.tsx @@ -1,39 +1,36 @@ -import {Fragment, useMemo} from 'react'; -import moment from 'moment-timezone'; - import {LinkButton} from 'sentry/components/button'; -import {DateTime} from 'sentry/components/dateTime'; import {t} from 'sentry/locale'; -import type {Event} from 'sentry/types/event'; import type {Group} from 'sentry/types/group'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {useLocation} from 'sentry/utils/useLocation'; -import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants'; import RelatedIssues from 'sentry/views/alerts/rules/metric/details/relatedIssues'; import RelatedTransactions from 'sentry/views/alerts/rules/metric/details/relatedTransactions'; -import {Dataset, TimePeriod} from 'sentry/views/alerts/rules/metric/types'; +import {Dataset} from 'sentry/views/alerts/rules/metric/types'; import {extractEventTypeFilterFromRule} from 'sentry/views/alerts/rules/metric/utils/getEventTypeFilter'; import {isCrashFreeAlert} from 'sentry/views/alerts/rules/metric/utils/isCrashFreeAlert'; import {useMetricRule} from 'sentry/views/alerts/rules/metric/utils/useMetricRule'; +import { + useMetricIssueAlertId, + useMetricTimePeriod, +} from 'sentry/views/issueDetails/metricIssues/utils'; import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; interface MetricIssuesSectionProps { - event: Event; group: Group; organization: Organization; project: Project; } -export default function MetricIssuesSection({ +export function MetricIssuesSection({ organization, - event, group, project, }: MetricIssuesSectionProps) { const location = useLocation(); - const ruleId = event.contexts?.metric_alert?.alert_rule_id; + + const ruleId = useMetricIssueAlertId({groupId: group.id}); const {data: rule} = useMetricRule( { orgSlug: organization.slug, @@ -48,33 +45,7 @@ export default function MetricIssuesSection({ enabled: !!ruleId, } ); - - const openPeriod = group.openPeriods?.[0]; - const timePeriod = useMemo((): TimePeriodType | null => { - if (!openPeriod) { - return null; - } - const start = openPeriod.start; - let end = openPeriod.end; - if (!end) { - end = new Date().toISOString(); - } - return { - start, - end, - period: TimePeriod.SEVEN_DAYS, - usingPeriod: false, - label: t('Custom time'), - display: ( - - - {' — '} - - - ), - custom: true, - }; - }, [openPeriod]); + const timePeriod = useMetricTimePeriod({openPeriod: group.openPeriods?.[0]}); if (!rule || !timePeriod) { return null; diff --git a/static/app/views/issueDetails/metricIssues/utils.tsx b/static/app/views/issueDetails/metricIssues/utils.tsx new file mode 100644 index 00000000000000..b21dc5b314c7a2 --- /dev/null +++ b/static/app/views/issueDetails/metricIssues/utils.tsx @@ -0,0 +1,77 @@ +import {Fragment, useMemo} from 'react'; +import moment from 'moment-timezone'; + +import {DateTime} from 'sentry/components/dateTime'; +import {t} from 'sentry/locale'; +import type {Event} from 'sentry/types/event'; +import type {GroupOpenPeriod} from 'sentry/types/group'; +import {useApiQuery} from 'sentry/utils/queryClient'; +import useOrganization from 'sentry/utils/useOrganization'; +import {useUser} from 'sentry/utils/useUser'; +import type {TimePeriodType} from 'sentry/views/alerts/rules/metric/details/constants'; +import {TimePeriod} from 'sentry/views/alerts/rules/metric/types'; +import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context'; +import {getGroupEventQueryKey} from 'sentry/views/issueDetails/utils'; + +export function useMetricIssueAlertId({groupId}: {groupId: string}): string | undefined { + /** + * This should be removed once the metric alert rule ID is set on the issue. + * This will fetch an event from the max range if the detector details + * are not available (e.g. time range has changed and page refreshed) + */ + const user = useUser(); + const organization = useOrganization(); + const {detectorDetails} = useIssueDetails(); + const {detectorId, detectorType} = detectorDetails; + + const hasMetricDetector = detectorId && detectorType === 'metric_alert'; + + const {data: event} = useApiQuery( + getGroupEventQueryKey({ + orgSlug: organization.slug, + groupId, + eventId: user.options.defaultIssueEvent, + environments: [], + }), + { + staleTime: Infinity, + enabled: !hasMetricDetector, + retry: false, + } + ); + + // Fall back to the fetched event in case the provider doesn't have the detector details + return hasMetricDetector ? detectorId : event?.contexts?.metric_alert?.alert_rule_id; +} + +export function useMetricTimePeriod({ + openPeriod, +}: { + openPeriod?: GroupOpenPeriod; +}): TimePeriodType | null { + return useMemo((): TimePeriodType | null => { + if (!openPeriod) { + return null; + } + const start = openPeriod.start; + let end = openPeriod.end; + if (!end) { + end = new Date().toISOString(); + } + return { + start, + end, + period: TimePeriod.SEVEN_DAYS, + usingPeriod: false, + label: t('Custom time'), + display: ( + + + {' — '} + + + ), + custom: true, + }; + }, [openPeriod]); +} diff --git a/static/app/views/issueDetails/streamline/eventDetailsHeader.tsx b/static/app/views/issueDetails/streamline/eventDetailsHeader.tsx index 7c4bba3a264b35..dfc2cbbb84fb54 100644 --- a/static/app/views/issueDetails/streamline/eventDetailsHeader.tsx +++ b/static/app/views/issueDetails/streamline/eventDetailsHeader.tsx @@ -16,6 +16,7 @@ import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig'; import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; +import {MetricIssuesChart} from 'sentry/views/issueDetails/metricIssues/metricIssueChart'; import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context'; import {EventGraph} from 'sentry/views/issueDetails/streamline/eventGraph'; import { @@ -25,7 +26,6 @@ import { import {IssueCronCheckTimeline} from 'sentry/views/issueDetails/streamline/issueCronCheckTimeline'; import IssueTagsPreview from 'sentry/views/issueDetails/streamline/issueTagsPreview'; import {IssueUptimeCheckTimeline} from 'sentry/views/issueDetails/streamline/issueUptimeCheckTimeline'; -import {MetricIssueChart} from 'sentry/views/issueDetails/streamline/metricIssueChart'; import {OccurrenceSummary} from 'sentry/views/issueDetails/streamline/occurrenceSummary'; import {getDetectorDetails} from 'sentry/views/issueDetails/streamline/sidebar/detectorSection'; import {ToggleSidebar} from 'sentry/views/issueDetails/streamline/sidebar/toggleSidebar'; @@ -121,7 +121,7 @@ export function EventDetailsHeader({group, event, project}: EventDetailsHeaderPr )} {issueTypeConfig.header.graph.type === 'detector-history' && ( - + )} {issueTypeConfig.header.graph.type === 'uptime-checks' && (