Skip to content

Commit 930bc9f

Browse files
upcoming: [DI-28773] - Support to select system channel based on service type controlled by LD flag (#13219)
* upcoming: [DI-28773] - Support to select system channel only for chosen service types based on LD flag * upcoming: [DI-28773] - Changeset * upcoming: [DI-28773] - Fix typecheck * upcoming: [DI-28773] - UT fix
1 parent 7c026eb commit 930bc9f

File tree

9 files changed

+103
-21
lines changed

9 files changed

+103
-21
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Upcoming Features
3+
---
4+
5+
Allow system channel selection based on selected service type in `CloudPulse` create and edit `alerts` ([#13219](https://github.com/linode/manager/pull/13219))

packages/manager/src/featureFlags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ interface AclpAlerting {
153153
editDisabledStatuses?: AlertStatusType[];
154154
notificationChannels: boolean;
155155
recentActivity: boolean;
156+
systemChannelSupportedServices?: CloudPulseServiceType[]; // linode, dbaas, etc.
156157
}
157158

158159
interface LimitsEvolution {

packages/manager/src/features/CloudPulse/Alerts/CreateAlert/CreateAlertDefinition.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ export const CreateAlertDefinition = () => {
165165
});
166166
resetField('scope', { defaultValue: null });
167167
resetField('entity_type', { defaultValue: 'linode' });
168+
resetField('channel_ids', { defaultValue: [] });
168169
}, [resetField]);
169170

170171
const handleEntityTypeChange = React.useCallback(() => {
@@ -256,7 +257,10 @@ export const CreateAlertDefinition = () => {
256257
serviceMetadataError={serviceMetadataError}
257258
serviceMetadataLoading={serviceMetadataLoading}
258259
/>
259-
<AddChannelListing name="channel_ids" />
260+
<AddChannelListing
261+
name="channel_ids"
262+
serviceType={serviceTypeWatcher}
263+
/>
260264
<ActionsPanel
261265
primaryButtonProps={{
262266
label: 'Submit',

packages/manager/src/features/CloudPulse/Alerts/CreateAlert/NotificationChannels/AddChannelListing.test.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ describe('Channel Listing component', () => {
4646

4747
const { getByText } =
4848
renderWithThemeAndHookFormContext<CreateAlertDefinitionForm>({
49-
component: <AddChannelListing name="channel_ids" />,
49+
component: (
50+
<AddChannelListing name="channel_ids" serviceType={'linode'} />
51+
),
5052
useFormOptions: {
5153
defaultValues: {
5254
channel_ids: [mockNotificationData[0].id],
@@ -59,10 +61,30 @@ describe('Channel Listing component', () => {
5961
expect(getByText(emailAddresses[1])).toBeInTheDocument();
6062
});
6163

64+
it('should disable the add notification button when service type is null', () => {
65+
const { getByText, getByRole } =
66+
renderWithThemeAndHookFormContext<CreateAlertDefinitionForm>({
67+
component: <AddChannelListing name="channel_ids" serviceType={null} />,
68+
useFormOptions: {
69+
defaultValues: {
70+
channel_ids: [],
71+
},
72+
},
73+
});
74+
expect(getByText('4. Notification Channels')).toBeVisible();
75+
const addButton = getByRole('button', {
76+
name: 'Add notification channel',
77+
});
78+
79+
expect(addButton).toBeDisabled();
80+
});
81+
6282
it('should remove the fields', async () => {
6383
const { getByTestId } =
6484
renderWithThemeAndHookFormContext<CreateAlertDefinitionForm>({
65-
component: <AddChannelListing name="channel_ids" />,
85+
component: (
86+
<AddChannelListing name="channel_ids" serviceType={'linode'} />
87+
),
6688
useFormOptions: {
6789
defaultValues: {
6890
channel_ids: [mockNotificationData[0].id],
@@ -82,7 +104,9 @@ describe('Channel Listing component', () => {
82104
const mockMaxLimit = 5;
83105
const { getByRole, findByText } =
84106
renderWithThemeAndHookFormContext<CreateAlertDefinitionForm>({
85-
component: <AddChannelListing name="channel_ids" />,
107+
component: (
108+
<AddChannelListing name="channel_ids" serviceType={'linode'} />
109+
),
86110
useFormOptions: {
87111
defaultValues: {
88112
channel_ids: Array(mockMaxLimit).fill(mockNotificationData[0].id), // simulate 5 channels

packages/manager/src/features/CloudPulse/Alerts/CreateAlert/NotificationChannels/AddChannelListing.tsx

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React from 'react';
44
import { Controller, useFormContext, useWatch } from 'react-hook-form';
55
import type { FieldPathByValue } from 'react-hook-form';
66

7+
import { useFlags } from 'src/hooks/useFlags';
78
import { useAllAlertNotificationChannelsQuery } from 'src/queries/cloudpulse/alerts';
89

910
import { channelTypeOptions, MULTILINE_ERROR_SEPARATOR } from '../../constants';
@@ -14,13 +15,21 @@ import { AddNotificationChannelDrawer } from './AddNotificationChannelDrawer';
1415
import { RenderChannelDetails } from './RenderChannelDetails';
1516

1617
import type { CreateAlertDefinitionForm } from '../types';
17-
import type { NotificationChannel } from '@linode/api-v4';
18+
import type {
19+
CloudPulseServiceType,
20+
NotificationChannel,
21+
} from '@linode/api-v4';
1822

1923
interface AddChannelListingProps {
2024
/**
2125
* FieldPathByValue for the notification channel ids
2226
*/
2327
name: FieldPathByValue<CreateAlertDefinitionForm, number[]>;
28+
29+
/**
30+
* Service type of the CloudPulse alert
31+
*/
32+
serviceType: CloudPulseServiceType | null;
2433
}
2534

2635
interface NotificationChannelsProps {
@@ -34,9 +43,10 @@ interface NotificationChannelsProps {
3443
notification: NotificationChannel;
3544
}
3645
export const AddChannelListing = (props: AddChannelListingProps) => {
37-
const { name } = props;
46+
const { name, serviceType } = props;
3847
const { control, setValue } = useFormContext<CreateAlertDefinitionForm>();
3948
const [openAddNotification, setOpenAddNotification] = React.useState(false);
49+
const flags = useFlags();
4050

4151
const notificationChannelWatcher = useWatch({
4252
control,
@@ -49,12 +59,31 @@ export const AddChannelListing = (props: AddChannelListingProps) => {
4959
} = useAllAlertNotificationChannelsQuery();
5060

5161
const notifications = React.useMemo(() => {
52-
return (
53-
notificationData?.filter(
54-
({ id }) => !notificationChannelWatcher.includes(id)
55-
) ?? []
56-
);
57-
}, [notificationChannelWatcher, notificationData]);
62+
if (!notificationData) return [];
63+
64+
return notificationData.filter(({ id, type }) => {
65+
if (notificationChannelWatcher.includes(id)) return false; // id already selected
66+
67+
const systemSupportedTypes =
68+
flags.aclpAlerting?.systemChannelSupportedServices;
69+
70+
if (serviceType && systemSupportedTypes?.includes(serviceType)) {
71+
return true; // show all types of channels if serviceType is supported
72+
}
73+
74+
if (serviceType && systemSupportedTypes) {
75+
return type === 'user'; // only show user-defined alert channels
76+
}
77+
78+
// if no flags, show all types
79+
return true;
80+
});
81+
}, [
82+
flags.aclpAlerting?.systemChannelSupportedServices,
83+
notificationChannelWatcher,
84+
notificationData,
85+
serviceType,
86+
]);
5887

5988
const selectedNotifications = React.useMemo(() => {
6089
return (
@@ -168,12 +197,14 @@ export const AddChannelListing = (props: AddChannelListingProps) => {
168197
<Button
169198
buttonType="outlined"
170199
data-qa-buttons="true"
171-
disabled={notificationChannelWatcher.length === 5}
200+
disabled={notificationChannelWatcher.length === 5 || !serviceType}
172201
onClick={handleOpenDrawer}
173202
size="medium"
174203
sx={{
175204
width:
176-
notificationChannelWatcher.length === 5 ? '215px' : '190px',
205+
notificationChannelWatcher.length === 5 || !serviceType
206+
? '215px'
207+
: '190px',
177208
}}
178209
tooltipText="You can add up to 5 notification channels."
179210
>

packages/manager/src/features/CloudPulse/Alerts/CreateAlert/NotificationChannels/AddNotificationChannelDrawer.test.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('AddNotificationChannelDrawer component', () => {
5959
);
6060
});
6161
it('should render the label component with happy path and able to select an option', async () => {
62-
const { findByRole, getByRole, getByTestId } = renderWithTheme(
62+
const { findByRole, getByRole, getByTestId, findByText } = renderWithTheme(
6363
<AddNotificationChannelDrawer
6464
handleCloseDrawer={vi.fn()}
6565
isNotificationChannelsError={false}
@@ -94,6 +94,9 @@ describe('AddNotificationChannelDrawer component', () => {
9494
})
9595
).toBeInTheDocument();
9696

97+
const type = await findByText(mockData[0].type);
98+
expect(type).toBeVisible();
99+
97100
await userEvent.click(
98101
await findByRole('option', {
99102
name: mockData[0].label,

packages/manager/src/features/CloudPulse/Alerts/CreateAlert/NotificationChannels/AddNotificationChannelDrawer.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,11 @@ export const AddNotificationChannelDrawer = (
8484
const channelLabelWatcher = useWatch({ control, name: 'label' });
8585
const selectedChannelTypeTemplate =
8686
channelTypeWatcher && templateData
87-
? templateData.filter(
88-
(template) => template.channel_type === channelTypeWatcher
89-
)
87+
? templateData
88+
.filter((template) => template.channel_type === channelTypeWatcher)
89+
.sort((channelA, channelB) =>
90+
channelA.type.localeCompare(channelB.type)
91+
) // sorting needed to group by type in Autocomplete
9092
: null;
9193

9294
const selectedTemplate = selectedChannelTypeTemplate?.find(
@@ -168,12 +170,15 @@ export const AddNotificationChannelDrawer = (
168170
? 'Error in fetching the data.'
169171
: '')
170172
}
173+
groupBy={({ type }) => type}
171174
key={channelTypeWatcher}
172175
label="Channel"
173176
onBlur={field.onBlur}
174-
onChange={(_, selected: { label: string }, reason) => {
177+
onChange={(_, selected, reason) => {
175178
field.onChange(
176-
reason === 'selectOption' ? selected.label : null
179+
reason === 'selectOption' && selected
180+
? selected.label
181+
: null
177182
);
178183
}}
179184
options={selectedChannelTypeTemplate ?? []}

packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ export const EditAlertDefinition = (props: EditAlertProps) => {
240240
serviceMetadataError={serviceMetadataError}
241241
serviceMetadataLoading={serviceMetadataLoading}
242242
/>
243-
<AddChannelListing name="channel_ids" />
243+
<AddChannelListing name="channel_ids" serviceType={serviceType} />
244244
<ActionsPanel
245245
primaryButtonProps={{
246246
label: 'Submit',

packages/manager/src/mocks/serverHandlers.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3578,6 +3578,15 @@ export const handlers = [
35783578
created_by: 'admin',
35793579
})
35803580
);
3581+
notificationChannels.push(
3582+
notificationChannelFactory.build({
3583+
label: 'System channel',
3584+
updated: '2023-11-05T04:00:00',
3585+
updated_by: 'user5',
3586+
created_by: 'admin',
3587+
type: 'system',
3588+
})
3589+
);
35813590
notificationChannels.push(...notificationChannelFactory.buildList(75));
35823591
return HttpResponse.json(makeResourcePage(notificationChannels));
35833592
}),

0 commit comments

Comments
 (0)