Skip to content

Commit e6c8712

Browse files
authored
feat(aci): Disable monitor creating/editing when user does not have alerts:write (#96687)
1 parent d811400 commit e6c8712

File tree

10 files changed

+311
-117
lines changed

10 files changed

+311
-117
lines changed

static/app/views/detectors/components/details/common/actions.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
import {Button} from 'sentry/components/core/button';
22
import {LinkButton} from 'sentry/components/core/button/linkButton';
3+
import {Link} from 'sentry/components/core/link';
34
import {IconEdit} from 'sentry/icons';
4-
import {t} from 'sentry/locale';
5+
import {t, tct} from 'sentry/locale';
56
import type {Detector} from 'sentry/types/workflowEngine/detectors';
67
import useOrganization from 'sentry/utils/useOrganization';
78
import {makeMonitorDetailsPathname} from 'sentry/views/detectors/pathnames';
9+
import {useCanEditDetector} from 'sentry/views/detectors/utils/useCanEditDetector';
810

911
export function DisableDetectorAction({detector}: {detector: Detector}) {
12+
const canEdit = useCanEditDetector({
13+
detectorType: detector.type,
14+
projectId: detector.projectId,
15+
});
16+
17+
if (!canEdit) {
18+
return null;
19+
}
20+
1021
/**
1122
* TODO: Implement disable detector
1223
*/
@@ -19,13 +30,25 @@ export function DisableDetectorAction({detector}: {detector: Detector}) {
1930

2031
export function EditDetectorAction({detector}: {detector: Detector}) {
2132
const organization = useOrganization();
33+
const canEdit = useCanEditDetector({
34+
detectorType: detector.type,
35+
projectId: detector.projectId,
36+
});
37+
38+
const permissionTooltipText = tct(
39+
'You do not have permission to edit this monitor. Ask your organization owner or manager to [settingsLink:enable monitor access] for you.',
40+
{settingsLink: <Link to={`/settings/${organization.slug}/#alertsMemberWrite`} />}
41+
);
2242

2343
return (
2444
<LinkButton
2545
to={`${makeMonitorDetailsPathname(organization.slug, detector.id)}edit/`}
2646
priority="primary"
2747
icon={<IconEdit />}
2848
size="sm"
49+
disabled={!canEdit}
50+
title={canEdit ? undefined : permissionTooltipText}
51+
tooltipProps={{isHoverable: true}}
2952
>
3053
{t('Edit')}
3154
</LinkButton>

static/app/views/detectors/components/details/uptime/index.spec.tsx

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {createContext, useContext} from 'react';
2+
3+
import type {Project} from 'sentry/types/project';
4+
import type {DetectorType} from 'sentry/types/workflowEngine/detectors';
5+
6+
type DetectorFormContextType = {
7+
detectorType: DetectorType;
8+
project: Project;
9+
};
10+
11+
const DetectorFormContext = createContext<DetectorFormContextType | null>(null);
12+
13+
export function DetectorFormProvider({
14+
detectorType,
15+
project,
16+
children,
17+
}: {
18+
children: React.ReactNode;
19+
detectorType: DetectorType;
20+
project: Project;
21+
}) {
22+
return (
23+
<DetectorFormContext.Provider value={{detectorType, project}}>
24+
{children}
25+
</DetectorFormContext.Provider>
26+
);
27+
}
28+
29+
export function useDetectorFormContext() {
30+
const context = useContext(DetectorFormContext);
31+
if (!context) {
32+
throw new Error('useDetectorFormContext must be used within a DetectorFormProvider');
33+
}
34+
35+
return context;
36+
}

static/app/views/detectors/components/forms/detectorBaseFields.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import * as Layout from 'sentry/components/layouts/thirds';
99
import {useFormField} from 'sentry/components/workflowEngine/form/useFormField';
1010
import {t} from 'sentry/locale';
1111
import useProjects from 'sentry/utils/useProjects';
12+
import {useDetectorFormContext} from 'sentry/views/detectors/components/forms/context';
13+
import {useCanEditDetector} from 'sentry/views/detectors/utils/useCanEditDetector';
1214

1315
export function DetectorBaseFields() {
1416
return (
@@ -43,14 +45,16 @@ export function DetectorBaseFields() {
4345

4446
function ProjectField() {
4547
const {projects, fetching} = useProjects();
48+
const {project, detectorType} = useDetectorFormContext();
49+
const canEditDetector = useCanEditDetector({projectId: project.id, detectorType});
4650

4751
return (
4852
<StyledProjectField
4953
inline={false}
5054
flexibleControlStateSize
5155
stacked
5256
projects={projects}
53-
groupProjects={project => (project.isMember ? 'member' : 'all')}
57+
groupProjects={p => (p.isMember ? 'member' : 'all')}
5458
groups={[
5559
{key: 'member', label: t('My Projects')},
5660
{key: 'all', label: t('All Projects')},
@@ -60,6 +64,17 @@ function ProjectField() {
6064
aria-label={t('Select Project')}
6165
disabled={fetching}
6266
size="sm"
67+
validate={() => {
68+
if (!canEditDetector) {
69+
return [
70+
[
71+
'projectId',
72+
t('You do not have permission to create or edit monitors in this project'),
73+
],
74+
];
75+
}
76+
return [];
77+
}}
6378
/>
6479
);
6580
}

0 commit comments

Comments
 (0)