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
2 changes: 1 addition & 1 deletion static/app/actionCreators/monitors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export async function setEnvironmentIsMuted(

try {
const resp = await api.requestPromise(
`/projects/${orgId}/${monitor.project.slug}/monitors/${monitor.slug}/environments/${environment}`,
`/projects/${orgId}/${monitor.project.slug}/monitors/${monitor.slug}/environments/${environment}/`,
{method: 'PUT', data: {isMuted}}
);
clearIndicators();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,71 @@ describe('CronDetectorDetails - check-ins', () => {
expect(screen.getByRole('button', {name: 'Enable'})).toBeInTheDocument();
});
});

describe('environment muting', () => {
it('refetches detector when environment is muted', async () => {
const detectorWithMultipleEnvs = CronDetectorFixture({
id: '1',
projectId: project.id,
dataSources: [
CronMonitorDataSourceFixture({
queryObj: {
...CronMonitorDataSourceFixture().queryObj,
environments: [
CronMonitorEnvironmentFixture({
name: 'production',
lastCheckIn: '2025-01-01T00:00:00Z',
isMuted: false,
}),
CronMonitorEnvironmentFixture({
name: 'staging',
lastCheckIn: '2025-01-01T00:00:00Z',
isMuted: false,
}),
],
},
}),
],
});

const muteRequest = MockApiClient.addMockResponse({
url: `/projects/org-slug/${project.slug}/monitors/${detectorWithMultipleEnvs.dataSources[0].queryObj.slug}/environments/production/`,
method: 'PUT',
body: {},
});

const detectorRefetchRequest = MockApiClient.addMockResponse({
url: `/organizations/org-slug/detectors/1/`,
body: detectorWithMultipleEnvs,
});

render(
<CronDetectorDetails detector={detectorWithMultipleEnvs} project={project} />
);

await screen.findByText('Recent Check-Ins');

expect(detectorRefetchRequest).toHaveBeenCalledTimes(1);

const envButtons = screen.getAllByRole('button', {
name: 'Monitor environment actions',
});
await userEvent.click(envButtons[0]!);

await userEvent.click(
await screen.findByRole('menuitemradio', {name: 'Mute Environment'})
);

expect(muteRequest).toHaveBeenCalledTimes(1);
expect(muteRequest).toHaveBeenCalledWith(
expect.stringContaining('/environments/production'),
expect.objectContaining({
method: 'PUT',
data: {isMuted: true},
})
);

expect(detectorRefetchRequest).toHaveBeenCalledTimes(2);
});
});
});
16 changes: 15 additions & 1 deletion static/app/views/detectors/components/details/cron/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {t, tn} from 'sentry/locale';
import type {Project} from 'sentry/types/project';
import type {CronDetector} from 'sentry/types/workflowEngine/detectors';
import toArray from 'sentry/utils/array/toArray';
import {useQueryClient} from 'sentry/utils/queryClient';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {
Expand All @@ -35,7 +36,10 @@ import {DisabledAlert} from 'sentry/views/detectors/components/details/common/di
import {DetectorExtraDetails} from 'sentry/views/detectors/components/details/common/extraDetails';
import {DetectorDetailsHeader} from 'sentry/views/detectors/components/details/common/header';
import {DetectorDetailsOpenPeriodIssues} from 'sentry/views/detectors/components/details/common/openPeriodIssues';
import {useDetectorQuery} from 'sentry/views/detectors/hooks';
import {
makeDetectorDetailsQueryKey,
useDetectorQuery,
} from 'sentry/views/detectors/hooks';
import {DetailsTimeline} from 'sentry/views/insights/crons/components/detailsTimeline';
import {DetailsTimelineLegend} from 'sentry/views/insights/crons/components/detailsTimelineLegend';
import {MonitorCheckIns} from 'sentry/views/insights/crons/components/monitorCheckIns';
Expand Down Expand Up @@ -69,6 +73,7 @@ export function CronDetectorDetails({detector, project}: CronDetectorDetailsProp
const userTimezone = useTimezone();
const [timezoneOverride, setTimezoneOverride] = useState(userTimezone);
const openDocsPanel = useDocsPanel(dataSource.queryObj.slug, project);
const queryClient = useQueryClient();

useDetectorQuery<CronDetector>(detector.id, {
staleTime: 0,
Expand All @@ -83,6 +88,14 @@ export function CronDetectorDetails({detector, project}: CronDetectorDetailsProp
},
});

const handleEnvironmentUpdated = useCallback(() => {
const queryKey = makeDetectorDetailsQueryKey({
orgSlug: organization.slug,
detectorId: detector.id,
});
queryClient.invalidateQueries({queryKey});
}, [queryClient, organization.slug, detector.id]);

const {checkinErrors, handleDismissError} = useMonitorProcessingErrors({
organization,
projectId: project.id,
Expand Down Expand Up @@ -174,6 +187,7 @@ export function CronDetectorDetails({detector, project}: CronDetectorDetailsProp
<DetailsTimeline
monitor={filteredMonitor}
onStatsLoaded={checkHasUnknown}
onEnvironmentUpdated={handleEnvironmentUpdated}
/>
<ErrorBoundary mini>
<DetectorDetailsOpenPeriodIssues
Expand Down
10 changes: 9 additions & 1 deletion static/app/views/insights/crons/components/detailsTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ import {CronServiceIncidents} from './serviceIncidents';

interface Props {
monitor: Monitor;
/**
* Called when an environment is updated (muted/unmuted/deleted).
*/
onEnvironmentUpdated?: () => void;
/**
* Called when monitor stats have been loaded for this timeline.
*/
onStatsLoaded?: (stats: MonitorBucket[]) => void;
}

export function DetailsTimeline({monitor, onStatsLoaded}: Props) {
export function DetailsTimeline({monitor, onStatsLoaded, onEnvironmentUpdated}: Props) {
const organization = useOrganization();
const location = useLocation();
const api = useApi();
Expand Down Expand Up @@ -89,6 +93,8 @@ export function DetailsTimeline({monitor, onStatsLoaded}: Props) {
}
: undefined;
});

onEnvironmentUpdated?.();
};

const handleToggleMuteEnvironment = async (env: string, isMuted: boolean) => {
Expand Down Expand Up @@ -119,6 +125,8 @@ export function DetailsTimeline({monitor, onStatsLoaded}: Props) {
}
: undefined;
});

onEnvironmentUpdated?.();
};

return (
Expand Down
Loading