2 ChangeDetectionStrategy,
9 } from '@angular/core';
11 import { Mutex } from 'async-mutex';
12 import _ from 'lodash';
13 import moment from 'moment';
14 import { Subscription } from 'rxjs';
16 import { PrometheusService } from '~/app/shared/api/prometheus.service';
17 import { SucceededActionLabelsI18n } from '~/app/shared/constants/app.constants';
18 import { Icons } from '~/app/shared/enum/icons.enum';
19 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
22 AlertmanagerSilenceMatcher
23 } from '~/app/shared/models/alertmanager-silence';
24 import { CdNotification } from '~/app/shared/models/cd-notification';
25 import { ExecutingTask } from '~/app/shared/models/executing-task';
26 import { FinishedTask } from '~/app/shared/models/finished-task';
27 import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
28 import { NotificationService } from '~/app/shared/services/notification.service';
29 import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
30 import { PrometheusNotificationService } from '~/app/shared/services/prometheus-notification.service';
31 import { SummaryService } from '~/app/shared/services/summary.service';
32 import { TaskMessageService } from '~/app/shared/services/task-message.service';
35 selector: 'cd-notifications-sidebar',
36 templateUrl: './notifications-sidebar.component.html',
37 styleUrls: ['./notifications-sidebar.component.scss'],
38 changeDetection: ChangeDetectionStrategy.OnPush
40 export class NotificationsSidebarComponent implements OnInit, OnDestroy {
41 @HostBinding('class.active') isSidebarOpened = false;
43 notifications: CdNotification[];
44 private interval: number;
45 private timeout: number;
47 executingTasks: ExecutingTask[] = [];
49 private subs = new Subscription();
62 public notificationService: NotificationService,
63 private summaryService: SummaryService,
64 private taskMessageService: TaskMessageService,
65 private prometheusNotificationService: PrometheusNotificationService,
66 private succeededLabels: SucceededActionLabelsI18n,
67 private authStorageService: AuthStorageService,
68 private prometheusAlertService: PrometheusAlertService,
69 private prometheusService: PrometheusService,
70 private ngZone: NgZone,
71 private cdRef: ChangeDetectorRef
73 this.notifications = [];
77 window.clearInterval(this.interval);
78 window.clearTimeout(this.timeout);
79 this.subs.unsubscribe();
83 this.last_task = window.localStorage.getItem('last_task');
85 const permissions = this.authStorageService.getPermissions();
86 if (permissions.prometheus.read && permissions.configOpt.read) {
87 this.triggerPrometheusAlerts();
88 this.ngZone.runOutsideAngular(() => {
89 this.interval = window.setInterval(() => {
90 this.ngZone.run(() => {
91 this.triggerPrometheusAlerts();
98 this.notificationService.data$.subscribe((notifications: CdNotification[]) => {
99 this.notifications = _.orderBy(notifications, ['timestamp'], ['desc']);
100 this.cdRef.detectChanges();
105 this.notificationService.panelState$.subscribe((state) => {
106 this.isSidebarOpened = state.isOpen && !state.useNewPanel;
107 this.cdRef.detectChanges();
112 this.summaryService.subscribe((summary) => {
113 this._handleTasks(summary.executing_tasks);
115 this.mutex.acquire().then((release) => {
117 summary.finished_tasks,
118 (task: FinishedTask) => !this.last_task || moment(task.end_time).isAfter(this.last_task)
119 ).forEach((task) => {
120 const config = this.notificationService.finishedTaskToNotification(task, task.success);
121 const notification = new CdNotification(config);
122 notification.timestamp = task.end_time;
123 notification.duration = task.duration;
125 if (!this.last_task || moment(task.end_time).isAfter(this.last_task)) {
126 this.last_task = task.end_time;
127 window.localStorage.setItem('last_task', this.last_task);
130 this.notificationService.save(notification);
133 this.cdRef.detectChanges();
141 _handleTasks(executingTasks: ExecutingTask[]) {
142 for (const executingTask of executingTasks) {
143 executingTask.description = this.taskMessageService.getRunningTitle(executingTask);
145 this.executingTasks = executingTasks;
148 private triggerPrometheusAlerts() {
149 this.prometheusAlertService.refresh();
150 this.prometheusNotificationService.refresh();
154 this.notificationService.removeAll();
157 remove(index: number) {
158 this.notificationService.remove(index);
162 this.notificationService.toggleSidebar(false, false);
165 trackByFn(index: number) {
169 silence(data: CdNotification) {
170 const datetimeFormat = 'YYYY-MM-DD HH:mm';
171 const resource = $localize`silence`;
172 const matcher: AlertmanagerSilenceMatcher = {
174 value: data['title'].split(' ')[0],
177 const silencePayload: AlertmanagerSilence = {
179 startsAt: moment(moment().format(datetimeFormat)).toISOString(),
180 endsAt: moment(moment().add(2, 'hours').format(datetimeFormat)).toISOString(),
181 createdBy: this.authStorageService.getUsername(),
182 comment: 'Silence created from the alert notification'
186 data.alertSilenced = true;
187 msg = msg.concat(` ${matcher.name} - ${matcher.value},`);
188 const title = `${this.succeededLabels.CREATED} ${resource} for ${msg.slice(0, -1)}`;
189 this.prometheusService.setSilence(silencePayload).subscribe((resp) => {
191 data.silenceId = resp.body['silenceId'];
193 this.notificationService.show(
194 NotificationType.success,
203 expire(data: CdNotification) {
204 data.alertSilenced = false;
205 this.prometheusService.expireSilence(data.silenceId).subscribe(
207 this.notificationService.show(
208 NotificationType.success,
209 `${this.succeededLabels.EXPIRED} ${data.silenceId}`,
216 resp['application'] = 'Prometheus';