Skip to content

Commit 68eb629

Browse files
ref(crons): Show processing errors on cron detectors list (#103527)
Add processing errors alert to the cron detectors list page to match the existing behavior on the insights crons overview page. Created a shared GlobalMonitorProcessingErrors component to avoid code duplication. Fixes [NEW-633](https://linear.app/getsentry/issue/NEW-633/general-cron-errors-should-be-shown-on-the-cron-detectors-specific)
1 parent a093a3b commit 68eb629

File tree

4 files changed

+69
-39
lines changed

4 files changed

+69
-39
lines changed

static/app/views/detectors/list/cron.spec.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ describe('CronDetectorsList', () => {
3333
body: UserFixture(),
3434
});
3535

36+
// Mock processing errors endpoint (no errors by default)
37+
MockApiClient.addMockResponse({
38+
url: '/organizations/org-slug/processing-errors/',
39+
body: [],
40+
});
41+
3642
// Ensure a project is selected for queries
3743
PageFiltersStore.onInitializeUrlState(PageFiltersFixture({projects: [1]}));
3844

static/app/views/detectors/list/cron.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {fadeIn} from 'sentry/styles/animations';
1515
import type {CronDetector, Detector} from 'sentry/types/workflowEngine/detectors';
1616
import {useDebouncedValue} from 'sentry/utils/useDebouncedValue';
1717
import {useDimensions} from 'sentry/utils/useDimensions';
18+
import usePageFilters from 'sentry/utils/usePageFilters';
1819
import {HeaderCell} from 'sentry/views/detectors/components/detectorListTable';
1920
import {DetectorListActions} from 'sentry/views/detectors/list/common/detectorListActions';
2021
import {DetectorListContent} from 'sentry/views/detectors/list/common/detectorListContent';
@@ -28,6 +29,7 @@ import {
2829
} from 'sentry/views/detectors/monitorViewContext';
2930
import {CronsLandingPanel} from 'sentry/views/insights/crons/components/cronsLandingPanel';
3031
import MonitorEnvironmentLabel from 'sentry/views/insights/crons/components/overviewTimeline/monitorEnvironmentLabel';
32+
import {GlobalMonitorProcessingErrors} from 'sentry/views/insights/crons/components/processingErrors/globalMonitorProcessingErrors';
3133
import {
3234
checkInStatusPrecedent,
3335
statusToText,
@@ -119,10 +121,13 @@ const DESCRIPTION = t(
119121
const DOCS_URL = 'https://docs.sentry.io/product/crons/';
120122

121123
export default function CronDetectorsList() {
124+
const {selection} = usePageFilters();
122125
const detectorListQuery = useDetectorListQuery({
123126
detectorFilter: 'monitor_check_in_failure',
124127
});
125128

129+
const selectedProjects = selection.projects.map(String);
130+
126131
const contextValue = useMemo<MonitorViewContextValue>(() => {
127132
return {
128133
additionalColumns: ADDITIONAL_COLUMNS,
@@ -160,6 +165,7 @@ export default function CronDetectorsList() {
160165
{t('Cron monitors have been moved from Insights to Monitors.')}
161166
</InsightsRedirectNotice>
162167
<DetectorListHeader showTimeRangeSelector showTypeFilter={false} />
168+
<GlobalMonitorProcessingErrors project={selectedProjects} />
163169
<DetectorListContent
164170
{...detectorListQuery}
165171
emptyState={<CronsLandingPanel />}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {deleteProjectProcessingErrorByType} from 'sentry/actionCreators/monitors';
2+
import {t} from 'sentry/locale';
3+
import {useApiQuery} from 'sentry/utils/queryClient';
4+
import useApi from 'sentry/utils/useApi';
5+
import useOrganization from 'sentry/utils/useOrganization';
6+
import {MonitorProcessingErrors} from 'sentry/views/insights/crons/components/processingErrors/monitorProcessingErrors';
7+
import {makeMonitorListErrorsQueryKey} from 'sentry/views/insights/crons/components/processingErrors/utils';
8+
import type {
9+
CheckinProcessingError,
10+
ProcessingErrorType,
11+
} from 'sentry/views/insights/crons/types';
12+
13+
interface GlobalMonitorProcessingErrorsProps {
14+
project?: string[];
15+
}
16+
17+
export function GlobalMonitorProcessingErrors({
18+
project,
19+
}: GlobalMonitorProcessingErrorsProps) {
20+
const api = useApi();
21+
const organization = useOrganization();
22+
23+
const processingErrorQueryKey = makeMonitorListErrorsQueryKey(organization, project);
24+
const {data: processingErrors, refetch: refetchErrors} = useApiQuery<
25+
CheckinProcessingError[]
26+
>(processingErrorQueryKey, {
27+
staleTime: 0,
28+
});
29+
30+
async function handleDismissError(errorType: ProcessingErrorType, projectId: string) {
31+
await deleteProjectProcessingErrorByType(
32+
api,
33+
organization.slug,
34+
projectId,
35+
errorType
36+
);
37+
await refetchErrors();
38+
}
39+
40+
if (!processingErrors?.length) {
41+
return null;
42+
}
43+
44+
return (
45+
<MonitorProcessingErrors
46+
checkinErrors={processingErrors}
47+
onDismiss={handleDismissError}
48+
>
49+
{t('Errors were encountered while ingesting check-ins for the selected projects')}
50+
</MonitorProcessingErrors>
51+
);
52+
}

static/app/views/insights/crons/views/overview.tsx

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import * as qs from 'query-string';
55
import {Alert} from '@sentry/scraps/alert';
66

77
import {openBulkEditMonitorsModal} from 'sentry/actionCreators/modal';
8-
import {deleteProjectProcessingErrorByType} from 'sentry/actionCreators/monitors';
98
import {Button} from 'sentry/components/core/button';
109
import {ButtonBar} from 'sentry/components/core/button/buttonBar';
1110
import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
@@ -30,7 +29,6 @@ import {useApiQuery} from 'sentry/utils/queryClient';
3029
import {decodeList, decodeScalar} from 'sentry/utils/queryString';
3130
import useRouteAnalyticsEventNames from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
3231
import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
33-
import useApi from 'sentry/utils/useApi';
3432
import {useLocation} from 'sentry/utils/useLocation';
3533
import {useNavigate} from 'sentry/utils/useNavigate';
3634
import useOrganization from 'sentry/utils/useOrganization';
@@ -42,22 +40,16 @@ import {
4240
import {NewMonitorButton} from 'sentry/views/insights/crons/components/newMonitorButton';
4341
import {OverviewTimeline} from 'sentry/views/insights/crons/components/overviewTimeline';
4442
import {OwnerFilter} from 'sentry/views/insights/crons/components/ownerFilter';
45-
import {MonitorProcessingErrors} from 'sentry/views/insights/crons/components/processingErrors/monitorProcessingErrors';
46-
import {makeMonitorListErrorsQueryKey} from 'sentry/views/insights/crons/components/processingErrors/utils';
43+
import {GlobalMonitorProcessingErrors} from 'sentry/views/insights/crons/components/processingErrors/globalMonitorProcessingErrors';
4744
import {MODULE_DESCRIPTION, MODULE_DOC_LINK} from 'sentry/views/insights/crons/settings';
48-
import type {
49-
CheckinProcessingError,
50-
Monitor,
51-
ProcessingErrorType,
52-
} from 'sentry/views/insights/crons/types';
45+
import type {Monitor} from 'sentry/views/insights/crons/types';
5346
import {makeMonitorListQueryKey} from 'sentry/views/insights/crons/utils';
5447

5548
const CronsListPageHeader = HookOrDefault({
5649
hookName: 'component:crons-list-page-header',
5750
});
5851

5952
function CronsOverview() {
60-
const api = useApi();
6153
const organization = useOrganization();
6254
const navigate = useNavigate();
6355
const location = useLocation();
@@ -76,13 +68,6 @@ function CronsOverview() {
7668
staleTime: 0,
7769
});
7870

79-
const processingErrorQueryKey = makeMonitorListErrorsQueryKey(organization, project);
80-
const {data: processingErrors, refetch: refetchErrors} = useApiQuery<
81-
CheckinProcessingError[]
82-
>(processingErrorQueryKey, {
83-
staleTime: 0,
84-
});
85-
8671
useRouteAnalyticsEventNames('monitors.page_viewed', 'Monitors: Page Viewed');
8772
useRouteAnalyticsParams({empty_state: !monitorList || monitorList.length === 0});
8873

@@ -96,16 +81,6 @@ function CronsOverview() {
9681
});
9782
};
9883

99-
async function handleDismissError(errorType: ProcessingErrorType, projectId: string) {
100-
await deleteProjectProcessingErrorByType(
101-
api,
102-
organization.slug,
103-
projectId,
104-
errorType
105-
);
106-
await refetchErrors();
107-
}
108-
10984
const showAddMonitor = !isValidPlatform(platform) || !isValidGuide(guide);
11085

11186
const page = (
@@ -171,18 +146,9 @@ function CronsOverview() {
171146
onSearch={handleSearch}
172147
/>
173148
</Filters>
174-
{!!processingErrors?.length && (
175-
<Alert.Container>
176-
<MonitorProcessingErrors
177-
checkinErrors={processingErrors}
178-
onDismiss={handleDismissError}
179-
>
180-
{t(
181-
'Errors were encountered while ingesting check-ins for the selected projects'
182-
)}
183-
</MonitorProcessingErrors>
184-
</Alert.Container>
185-
)}
149+
<Alert.Container>
150+
<GlobalMonitorProcessingErrors project={project} />
151+
</Alert.Container>
186152
{isPending ? (
187153
<LoadingIndicator />
188154
) : monitorList?.length ? (

0 commit comments

Comments
 (0)