Skip to content

Commit de16401

Browse files
Feature/support user channels (#13254)
* upcoming: [DI-29113] - Support for User Channel Details in Alert flows * add changeset * add support for new alerts object in Notification Channel * changesets * remove changesets * remove unnecessary comment --------- Co-authored-by: Ankita <ankitaan@akamai.com>
1 parent 9079bc9 commit de16401

File tree

12 files changed

+154
-51
lines changed

12 files changed

+154
-51
lines changed

packages/api-v4/src/cloudpulse/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,13 @@ export interface Alert {
282282
updated_by: string;
283283
}
284284

285-
interface NotificationChannelAlerts {
286-
id: number;
287-
label: string;
285+
interface NotificationChannelAlertInfo {
286+
alert_count: number;
288287
type: 'alerts-definitions';
289288
url: string;
290289
}
291290
interface NotificationChannelBase {
292-
alerts: NotificationChannelAlerts[];
291+
alerts: NotificationChannelAlertInfo;
293292
channel_type: ChannelType;
294293
created: string;
295294
created_by: string;

packages/manager/cypress/e2e/core/cloudpulse/alert-notification-channel-list.spec.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,11 @@ const notificationChannels = notificationChannelFactory
4141
.buildList(26)
4242
.map((ch, i) => {
4343
const isEmail = i % 2 === 0;
44-
const alerts = Array.from({ length: isEmail ? 5 : 3 }).map((_, idx) => ({
45-
id: idx + 1,
46-
label: `Alert-${idx + 1}`,
44+
const alerts = {
45+
alert_count: isEmail ? 5 : 3,
46+
url: `monitor/alert-channels/${i + 1}/alerts`,
4747
type: 'alerts-definitions',
48-
url: 'Sample',
49-
}));
48+
};
5049

5150
if (isEmail) {
5251
return {
@@ -147,7 +146,7 @@ const VerifyChannelSortingParams = (
147146
);
148147

149148
const order = sortOrderMap[sortOrder];
150-
const orderBy = LabelLookup[columnLabel];
149+
const orderBy = encodeURIComponent(LabelLookup[columnLabel]);
151150

152151
cy.url().should(
153152
'endWith',
@@ -215,16 +214,7 @@ describe('Notification Channel Listing Page', () => {
215214
}
216215

217216
// Alerts list
218-
expect(item.alerts.length).to.eq(expected.alerts.length);
219-
220-
item.alerts.forEach((alert, aIndex) => {
221-
const expAlert = expected.alerts[aIndex];
222-
223-
expect(alert.id).to.eq(expAlert.id);
224-
expect(alert.label).to.eq(expAlert.label);
225-
expect(alert.type).to.eq(expAlert.type);
226-
expect(alert.url).to.eq(expAlert.url);
227-
});
217+
expect(item.alerts.alert_count).to.eq(expected.alerts.alert_count);
228218
});
229219
});
230220
});
@@ -254,7 +244,9 @@ describe('Notification Channel Listing Page', () => {
254244

255245
cy.wrap($row).within(() => {
256246
cy.findByText(expected.label).should('be.visible');
257-
cy.findByText(String(expected.alerts.length)).should('be.visible');
247+
cy.findByText(String(expected.alerts.alert_count)).should(
248+
'be.visible'
249+
);
258250
cy.findByText('Email').should('be.visible');
259251
cy.get('td').eq(3).should('have.text', expected.created_by);
260252
cy.findByText(
@@ -284,11 +276,11 @@ describe('Notification Channel Listing Page', () => {
284276
{
285277
column: 'Alerts',
286278
ascending: [...notificationChannels]
287-
.sort((a, b) => a.alerts.length - b.alerts.length)
279+
.sort((a, b) => a.alerts.alert_count - b.alerts.alert_count)
288280
.map((ch) => ch.id),
289281

290282
descending: [...notificationChannels]
291-
.sort((a, b) => b.alerts.length - a.alerts.length)
283+
.sort((a, b) => b.alerts.alert_count - a.alerts.alert_count)
292284
.map((ch) => ch.id),
293285
},
294286

packages/manager/src/factories/cloudpulse/channels.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ import type { NotificationChannel } from '@linode/api-v4';
44

55
export const notificationChannelFactory =
66
Factory.Sync.makeFactory<NotificationChannel>({
7-
alerts: [
8-
{
9-
id: Number(Factory.each((i) => i)),
10-
label: String(Factory.each((id) => `Alert-${id}`)),
11-
type: 'alerts-definitions',
12-
url: 'Sample',
13-
},
14-
],
7+
alerts: {
8+
type: 'alerts-definitions',
9+
alert_count: 1,
10+
url: 'monitor/alert-channels/{id}/alerts',
11+
},
1512
channel_type: 'email',
1613
content: {
1714
email: {

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { screen } from '@testing-library/react';
12
import * as React from 'react';
23

34
import { notificationChannelFactory } from 'src/factories/cloudpulse/channels';
@@ -21,4 +22,21 @@ describe('RenderChannelDetails component', () => {
2122
expect(container.getByText(emailAddresses[0])).toBeVisible();
2223
expect(container.getByText(emailAddresses[1])).toBeVisible();
2324
});
25+
it('should render the email channel with usernames if details is present', () => {
26+
const usernames = ['user1', 'user2'];
27+
const mockDataWithDetails: NotificationChannel =
28+
notificationChannelFactory.build({
29+
channel_type: 'email',
30+
content: {},
31+
details: {
32+
email: {
33+
usernames,
34+
recipient_type: 'user',
35+
},
36+
},
37+
});
38+
renderWithTheme(<RenderChannelDetails template={mockDataWithDetails} />);
39+
expect(screen.getByText(usernames[0])).toBeVisible();
40+
expect(screen.getByText(usernames[1])).toBeVisible();
41+
});
2442
});

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Chip } from '@mui/material';
22
import * as React from 'react';
33

4+
import { shouldUseContentsForEmail } from '../../Utils/utils';
5+
46
import type { NotificationChannel } from '@linode/api-v4';
57

68
interface RenderChannelDetailProps {
@@ -12,9 +14,21 @@ interface RenderChannelDetailProps {
1214
export const RenderChannelDetails = (props: RenderChannelDetailProps) => {
1315
const { template } = props;
1416
if (template.channel_type === 'email') {
15-
return template.content?.email.email_addresses.map((value, index) => (
16-
<Chip key={index} label={value} />
17-
));
17+
const contentEmail = template.content?.email;
18+
const detailEmail = template.details?.email;
19+
const useContents = shouldUseContentsForEmail(template);
20+
21+
const recipients = useContents
22+
? (contentEmail?.email_addresses ?? [])
23+
: (detailEmail?.usernames ?? []);
24+
25+
return (
26+
<>
27+
{recipients.map((value) => (
28+
<Chip key={value} label={value} />
29+
))}
30+
</>
31+
);
1832
}
1933
return null;
2034
};

packages/manager/src/features/CloudPulse/Alerts/NotificationChannels/NotificationsChannelsListing/NotificationChannelListTable.test.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { NotificationChannelListTable } from './NotificationChannelListTable';
1010

1111
const mockScrollToElement = vi.fn();
1212

13-
const ALERT_TYPE = 'alerts-definitions';
14-
1513
describe('NotificationChannelListTable', () => {
1614
it('should render the notification channel table headers', () => {
1715
renderWithTheme(
@@ -127,11 +125,7 @@ describe('NotificationChannelListTable', () => {
127125

128126
it('should display correct alerts count', () => {
129127
const channel = notificationChannelFactory.build({
130-
alerts: [
131-
{ id: 1, label: 'Alert 1', type: ALERT_TYPE, url: 'url1' },
132-
{ id: 2, label: 'Alert 2', type: ALERT_TYPE, url: 'url2' },
133-
{ id: 3, label: 'Alert 3', type: ALERT_TYPE, url: 'url3' },
134-
],
128+
alerts: { alert_count: 3 },
135129
});
136130

137131
renderWithTheme(

packages/manager/src/features/CloudPulse/Alerts/NotificationChannels/NotificationsChannelsListing/NotificationChannelTableRow.test.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ describe('NotificationChannelTableRow', () => {
1818
it('should render a notification channel row with all fields', () => {
1919
const updated = new Date().toISOString();
2020
const channel = notificationChannelFactory.build({
21-
alerts: [
22-
{ id: 1, label: 'Alert 1', type: 'alerts-definitions', url: 'url1' },
23-
{ id: 2, label: 'Alert 2', type: 'alerts-definitions', url: 'url2' },
24-
],
21+
alerts: {
22+
type: 'alerts-definitions',
23+
alert_count: 2,
24+
url: 'monitor/alert-channels/{id}/alerts',
25+
},
2526
channel_type: 'email',
2627
created_by: 'user1',
2728
label: 'Test Channel',
@@ -142,7 +143,7 @@ describe('NotificationChannelTableRow', () => {
142143

143144
it('should render zero alerts count when no alerts are associated', () => {
144145
const channel = notificationChannelFactory.build({
145-
alerts: [],
146+
alerts: { alert_count: 0 },
146147
});
147148

148149
renderWithTheme(

packages/manager/src/features/CloudPulse/Alerts/NotificationChannels/NotificationsChannelsListing/NotificationChannelTableRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const NotificationChannelTableRow = (
5252
{label}
5353
</Link>
5454
</TableCell>
55-
<TableCell>{alerts.length}</TableCell>
55+
<TableCell>{alerts.alert_count}</TableCell>
5656
<TableCell>{channelTypeMap[channel_type]}</TableCell>
5757
<TableCell>{created_by}</TableCell>
5858
<TableCell>

packages/manager/src/features/CloudPulse/Alerts/NotificationChannels/NotificationsChannelsListing/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { NotificationChannel } from '@linode/api-v4';
22

33
type ChannelListingTableLabel = {
44
colName: string;
5-
label: keyof NotificationChannel;
5+
label: `${keyof NotificationChannel}:${string}` | keyof NotificationChannel;
66
};
77

88
export const ChannelListingTableLabelMap: ChannelListingTableLabel[] = [
@@ -12,7 +12,7 @@ export const ChannelListingTableLabelMap: ChannelListingTableLabel[] = [
1212
},
1313
{
1414
colName: 'Alerts',
15-
label: 'alerts',
15+
label: 'alerts:alert_count',
1616
},
1717
{
1818
colName: 'Channel Type',

packages/manager/src/features/CloudPulse/Alerts/Utils/utils.test.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { regionFactory } from '@linode/utilities';
22
import { act, renderHook } from '@testing-library/react';
33

4-
import { alertFactory, serviceTypesFactory } from 'src/factories';
4+
import { alertFactory, notificationChannelFactory, serviceTypesFactory } from 'src/factories';
55

66
import { useContextualAlertsState } from '../../Utils/utils';
77
import { transformDimensionValue } from '../CreateAlert/Criteria/DimensionFilterValue/utils';
@@ -17,6 +17,7 @@ import {
1717
getSchemaWithEntityIdValidation,
1818
getServiceTypeLabel,
1919
handleMultipleError,
20+
shouldUseContentsForEmail,
2021
} from './utils';
2122

2223
import type { AlertValidationSchemaProps } from './utils';
@@ -497,3 +498,60 @@ describe('transformDimensionValue', () => {
497498
).toBe('Test_value');
498499
});
499500
});
501+
502+
describe('shouldUseContentsForEmail', () => {
503+
it('should return false for email channel with valid usernames in details', () => {
504+
const notificationChannel = notificationChannelFactory.build({
505+
channel_type: 'email',
506+
details: {
507+
email: {
508+
usernames: ['user1', 'user2'],
509+
},
510+
},
511+
});
512+
expect(shouldUseContentsForEmail(notificationChannel)).toBe(false);
513+
});
514+
515+
it('should return true for email channel with undefined details', () => {
516+
const notificationChannel = notificationChannelFactory.build({
517+
channel_type: 'email',
518+
details: undefined,
519+
});
520+
expect(shouldUseContentsForEmail(notificationChannel)).toBe(true);
521+
});
522+
523+
it('should return true for email channel with undefined details.email', () => {
524+
const notificationChannel = notificationChannelFactory.build({
525+
channel_type: 'email',
526+
details: {
527+
email: undefined,
528+
},
529+
});
530+
expect(shouldUseContentsForEmail(notificationChannel)).toBe(true);
531+
});
532+
533+
it('should return true for email channel with undefined usernames', () => {
534+
const notificationChannel = notificationChannelFactory.build({
535+
channel_type: 'email',
536+
details: {
537+
email: {
538+
usernames: undefined,
539+
},
540+
},
541+
});
542+
expect(shouldUseContentsForEmail(notificationChannel)).toBe(true);
543+
});
544+
545+
it('should return true for email channel with empty usernames array', () => {
546+
const notificationChannel = notificationChannelFactory.build({
547+
channel_type: 'email',
548+
details: {
549+
email: {
550+
usernames: [],
551+
recipient_type: 'admin_users',
552+
},
553+
},
554+
});
555+
expect(shouldUseContentsForEmail(notificationChannel)).toBe(true);
556+
});
557+
});

0 commit comments

Comments
 (0)