Skip to content

Commit 6f49458

Browse files
authored
Merge pull request #2021 from devtron-labs/feat/notifications
feat: add support for cluster and prod/non-prod env in notifications
2 parents c6b3040 + 36d320a commit 6f49458

File tree

7 files changed

+127
-33
lines changed

7 files changed

+127
-33
lines changed

.storybook/preview.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ const preview: Preview = {
1212
date: /Date$/i,
1313
},
1414
},
15+
backgrounds: {
16+
values: [
17+
{
18+
name: 'Light',
19+
value: 'var(--N0)',
20+
},
21+
{
22+
name: 'Dark',
23+
value: 'var(--N700)',
24+
},
25+
],
26+
default: 'Light',
27+
},
1528
},
1629
tags: ['autodocs'],
1730
decorators: (Story) => (

src/App.tsx

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { lazy, Suspense, useRef, useState, useEffect } from 'react'
1818
import { Route, Switch, Redirect, useHistory, useLocation } from 'react-router-dom'
19-
import './css/application.scss';
19+
import './css/application.scss'
2020
import {
2121
showError,
2222
BreadcrumbStore,
@@ -26,9 +26,10 @@ import {
2626
useUserEmail,
2727
URLS as CommonURLS,
2828
ToastManager,
29-
ToastVariantType
29+
ToastVariantType,
3030
} from '@devtron-labs/devtron-fe-common-lib'
3131
import { ReactComponent as ICSparkles } from '@Icons/ic-sparkles.svg'
32+
import { ReactComponent as ICArrowClockwise } from '@Icons/ic-arrow-clockwise.svg'
3233
import { useRegisterSW } from 'virtual:pwa-register/react'
3334
import {
3435
useOnline,
@@ -78,19 +79,22 @@ export default function App() {
7879
useEffect(() => {
7980
if (didMountRef.current) {
8081
if (!isOnline) {
82+
onlineToast(
83+
{
84+
variant: ToastVariantType.error,
85+
title: 'You are offline!',
86+
description: 'You are not seeing real-time data and any changes you make will not be saved.',
87+
},
88+
{
89+
autoClose: false,
90+
},
91+
)
92+
} else {
8193
onlineToast({
82-
variant: ToastVariantType.error,
83-
title: 'You are offline!',
84-
description: 'You are not seeing real-time data and any changes you make will not be saved.',
85-
}, {
86-
autoClose: false
94+
variant: ToastVariantType.success,
95+
title: 'Connected!',
96+
description: "You're back online.",
8797
})
88-
} else {
89-
onlineToast({
90-
variant: ToastVariantType.success,
91-
title: 'Connected!',
92-
description: "You're back online.",
93-
})
9498
}
9599
} else {
96100
didMountRef.current = true
@@ -245,6 +249,7 @@ export default function App() {
245249
text: 'Reload',
246250
dataTestId: 'reload-btn',
247251
onClick: update,
252+
startIcon: <ICArrowClockwise />,
248253
},
249254
icon: <ICSparkles />,
250255
progressBarBg: UPDATE_AVAILABLE_TOAST_PROGRESS_BG,
@@ -281,6 +286,7 @@ export default function App() {
281286
text: 'Reload',
282287
dataTestId: 'reload-btn',
283288
onClick: reloadLocation,
289+
startIcon: <ICArrowClockwise />,
284290
},
285291
icon: <ICSparkles />,
286292
progressBarBg: UPDATE_AVAILABLE_TOAST_PROGRESS_BG,

src/components/notifications/AddNotification.tsx

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
CHECKBOX_VALUE,
2828
getIsRequestAborted,
2929
CiPipelineSourceConfig,
30+
SelectAllGroupedResourceIdentifiers,
3031
ToastVariantType,
3132
ToastManager,
3233
} from '@devtron-labs/devtron-fe-common-lib'
@@ -56,13 +57,15 @@ import { getAppListMin, getEnvironmentListMin } from '../../services/service'
5657
import { SMTPConfigModal } from './SMTPConfigModal'
5758
import { EMAIL_AGENT } from './types'
5859
import { WebhookConfigModal } from './WebhookConfigModal'
60+
import { getClusterListMin } from '@Components/ClusterNodes/clusterNodes.service'
5961

6062
interface AddNotificationsProps extends RouteComponentProps<{}> {}
6163

62-
enum FilterOptions {
64+
export enum FilterOptions {
6365
ENVIRONMENT = 'environment',
6466
APPLICATION = 'application',
6567
PROJECT = 'project',
68+
CLUSTER = 'cluster'
6669
}
6770
interface Options {
6871
environment: {
@@ -80,6 +83,11 @@ interface Options {
8083
label: string
8184
type: string
8285
}[]
86+
cluster: {
87+
value: number
88+
label: string
89+
type: string
90+
}[]
8391
}
8492
export interface PipelineType {
8593
checkbox: {
@@ -145,6 +153,7 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
145153
{ value: 1, label: 'application', type: 'main' },
146154
{ value: 2, label: 'project', type: 'main' },
147155
{ value: 3, label: 'environment', type: 'main' },
156+
{ value: 4, label: 'cluster', type: 'main' },
148157
]
149158

150159
filterOptionsInner = []
@@ -169,7 +178,7 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
169178
selectedChannels: [],
170179
pipelineList: [],
171180
emailAgentConfigId: 0,
172-
options: { environment: [], application: [], project: [] },
181+
options: { environment: [], application: [], project: [], cluster: [] },
173182
selectedEmailAgent: EMAIL_AGENT.SES,
174183
showWebhookConfigModal: false,
175184
}
@@ -192,7 +201,7 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
192201
}
193202

194203
getInitialData() {
195-
this.getEnvTeamData()
204+
this.getEnvTeamAndClusterData()
196205
getAddNotificationInitData().then((result) => {
197206
this.setState({
198207
sesConfigOptions: result.sesConfigOptions,
@@ -522,23 +531,41 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
522531
}
523532
}
524533

525-
getEnvTeamData(): void {
526-
Promise.all([getEnvironmentListMin(), getTeamListMin()]).then(([environments, teams]) => {
534+
getEnvTeamAndClusterData(): void {
535+
Promise.all([getEnvironmentListMin(), getTeamListMin(), getClusterListMin()]).then(([environments, teams, clusters]) => {
527536
const state = { ...this.state }
528-
state.options.environment = environments.result.map((elem) => {
537+
state.options.environment = [
538+
{
539+
environment_name: 'All non-prod environments',
540+
// parseInt can be removed once BE supports identifiers instead
541+
id: parseInt(SelectAllGroupedResourceIdentifiers.allExistingAndFutureNonProdEnvironments),
542+
},
543+
{
544+
environment_name: 'All prod environments',
545+
id: parseInt(SelectAllGroupedResourceIdentifiers.allExistingAndFutureProdEnvironments),
546+
},
547+
...(environments?.result ?? []),
548+
].map((elem) => {
529549
return {
530550
label: `${elem.environment_name.toLowerCase()}`,
531551
value: elem.id,
532552
type: FilterOptions.ENVIRONMENT,
533553
}
534554
})
535-
state.options.project = teams.result.map((elem) => {
555+
state.options.project = (teams?.result ?? []).map((elem) => {
536556
return {
537557
label: `${elem.name.toLowerCase()}`,
538558
value: elem.id,
539559
type: FilterOptions.PROJECT,
540560
}
541561
})
562+
state.options.cluster = (clusters?.result ?? []).map(({ name, id }) => {
563+
return {
564+
label: `${name.toLowerCase()}`,
565+
value: id,
566+
type: FilterOptions.CLUSTER,
567+
}
568+
})
542569
this.setState(state)
543570
})
544571
}
@@ -595,10 +622,16 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
595622
input.length === 0
596623
? this.state.options.project
597624
: this.state.options.project.filter((filter) => filter.label.indexOf(input) >= 0)
625+
} else if (unsavedFilter.type === FilterOptions.CLUSTER) {
626+
options =
627+
input.length === 0
628+
? this.state.options.cluster
629+
: this.state.options.cluster.filter((filter) => filter.label.indexOf(input) >= 0)
598630
} else {
599631
options = input.length <= 2 ? [] : this.state.options.application
600632
}
601633
}
634+
602635
return (
603636
<div className="dc__position-rel">
604637
<div
@@ -642,7 +675,7 @@ export class AddNotification extends Component<AddNotificationsProps, AddNotific
642675
/>
643676
) : (
644677
!this.state.appliedFilters.length && (
645-
<span>Filter by Project, applications and environment, search by name.</span>
678+
<span>Filter by Project, application, cluster and environment, search by name.</span>
646679
)
647680
)}
648681
</div>

src/components/notifications/NotificationTab.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ export interface NotificationConfiguration {
7373
project: { id: number; name: string }[]
7474
application: { id: number; name: string }[]
7575
environment: { id: number; name: string }[]
76+
cluster: {
77+
id: number
78+
name: string
79+
}[]
7680
}
7781
singleDeletedId: number
7882
isVirtualEnvironment?: boolean
@@ -640,7 +644,8 @@ export class NotificationTab extends Component<any, NotificationTabState> {
640644
{row.pipelineName ? row.pipelineName : ''}
641645
{row.appliedFilters.environment?.length ||
642646
row.appliedFilters.application.length ||
643-
row.appliedFilters.project?.length ? (
647+
row.appliedFilters.project?.length ||
648+
row.appliedFilters.cluster?.length ? (
644649
<>
645650
<i>All current and future pipelines matching.</i>
646651
<div className="dc__devtron-tag__container">
@@ -677,6 +682,17 @@ export class NotificationTab extends Component<any, NotificationTabState> {
677682
</span>
678683
)
679684
})}
685+
{row.appliedFilters.cluster.map((element) => {
686+
return (
687+
<span
688+
data-testid={`${row.pipelineType}-${element.name}`}
689+
key={element.name}
690+
className="dc__devtron-tag mr-5"
691+
>
692+
Cluster:{element.name}
693+
</span>
694+
)
695+
})}
680696
</div>{' '}
681697
</>
682698
) : null}

src/components/notifications/notifications.service.ts

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { get, post, trash, put, ResponseType, sortCallback } from '@devtron-labs/devtron-fe-common-lib'
1818
import { Routes } from '../../config/constants'
1919
import { NotificationConfiguration } from './NotificationTab'
20-
import { PipelineType } from './AddNotification'
20+
import { FilterOptions, PipelineType } from './AddNotification'
2121
import { SMTPConfigResponseType, WebhookAttributesResponseType } from './types'
2222

2323
interface UpdateNotificationEvent {
@@ -97,14 +97,22 @@ function createSaveNotificationPayload(selectedPipelines, providers, sesConfigId
9797
eventTypeIds.push(3)
9898
}
9999

100-
const teamId = config.appliedFilters.filter((filter) => filter.type === 'project').map((p) => p.id)
101-
const appId = config.appliedFilters.filter((filter) => filter.type === 'application').map((app) => app.id)
102-
const envId = config.appliedFilters.filter((filter) => filter.type === 'environment').map((e) => e.id)
100+
const teamId = config.appliedFilters
101+
.filter((filter) => filter.type === FilterOptions.PROJECT)
102+
.map((p) => p.id)
103+
const appId = config.appliedFilters
104+
.filter((filter) => filter.type === FilterOptions.APPLICATION)
105+
.map((app) => app.id)
106+
const envId = config.appliedFilters.filter((filter) => filter.type === FilterOptions.ENVIRONMENT).map((e) => e.id)
107+
const clusterId = config.appliedFilters
108+
.filter((filter) => filter.type === FilterOptions.CLUSTER)
109+
.map((e) => e.id)
103110

104111
return {
105112
teamId,
106113
appId,
107114
envId,
115+
clusterId,
108116
pipelineId: config.pipelineId,
109117
pipelineType: config.type,
110118
eventTypeIds,
@@ -243,6 +251,7 @@ export function getNotificationConfigurations(offset: number, pageSize: number):
243251
project: config.team || [],
244252
application: config.app || [],
245253
environment: config.environment || [],
254+
cluster: config.cluster || [],
246255
},
247256
isVirtualEnvironment: config?.pipeline?.isVirtualEnvironment,
248257
}
@@ -451,39 +460,49 @@ function getChannelsAndEmails(): Promise<GetChannelsResponseType> {
451460
export function getPipelines(filters): Promise<GetPipelinesResponseType> {
452461
const URL = `${Routes.NOTIFIER}/search`
453462
const payload = {
454-
teamId: filters.filter((p) => p.type == 'project').map((p) => p.value),
455-
envId: filters.filter((p) => p.type == 'environment').map((p) => p.value),
456-
appId: filters.filter((p) => p.type == 'application').map((p) => p.value),
463+
teamId: filters.filter((p) => p.type === FilterOptions.PROJECT).map((p) => p.value),
464+
envId: filters.filter((p) => p.type === FilterOptions.ENVIRONMENT).map((p) => p.value),
465+
appId: filters.filter((p) => p.type === FilterOptions.APPLICATION).map((p) => p.value),
466+
clusterId: filters.filter((p) => p.type === FilterOptions.CLUSTER).map(p => p.value),
457467
pipelineName: filters.find((p) => p.type == 'pipeline')?.value,
458468
}
459469
return post(URL, payload).then((response) => {
460470
const parsedResult = response.result?.map((row) => {
461471
const projects = row.team
462472
? row.team.map((team) => {
463473
return {
464-
type: 'project',
474+
type: FilterOptions.PROJECT,
465475
...team,
466476
}
467477
})
468478
: []
469479
const app = row.app
470480
? row.app.map((team) => {
471481
return {
472-
type: 'application',
482+
type: FilterOptions.APPLICATION,
473483
...team,
474484
}
475485
})
476486
: []
477487
const environment = row.environment
478488
? row.environment?.map((team) => {
479489
return {
480-
type: 'environment',
490+
type: FilterOptions.ENVIRONMENT,
481491
...team,
482492
}
483493
})
484494
: []
495+
const cluster = row.cluster
496+
? row.cluster?.map((cluster) => {
497+
return {
498+
type: FilterOptions.CLUSTER,
499+
...cluster,
500+
}
501+
})
502+
: []
503+
485504
return {
486-
appliedFilters: projects.concat(app, environment),
505+
appliedFilters: projects.concat(app, environment, cluster),
487506
checkbox: { isChecked: false, value: 'INTERMEDIATE' },
488507
pipelineId: row.pipeline?.id,
489508
appName: row?.pipeline?.appName,

src/components/v2/devtronStackManager/DevtronStackManager.service.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import { get, post, ToastManager, ToastVariantType } from '@devtron-labs/devtron-fe-common-lib'
1818
import { ReactComponent as ICSparkles } from '@Icons/ic-sparkles.svg'
19+
import { ReactComponent as ICArrowClockwise } from '@Icons/ic-arrow-clockwise.svg'
1920
import { ModuleNameMap, Routes, UPDATE_AVAILABLE_TOAST_PROGRESS_BG } from '../../../config'
2021
import {
2122
AllModuleInfoResponse,
@@ -110,6 +111,7 @@ export const getModuleInfo = async (moduleName: string, forceReload?: boolean):
110111
text: 'Reload',
111112
dataTestId: 'reload-button',
112113
onClick: reloadLocation,
114+
startIcon: <ICArrowClockwise />,
113115
},
114116
icon: <ICSparkles />,
115117
progressBarBg: UPDATE_AVAILABLE_TOAST_PROGRESS_BG,

0 commit comments

Comments
 (0)