import { CdFormGroup } from '../../../shared/forms/cd-form-group';
import { CdValidators } from '../../../shared/forms/cd-validators';
import { NotificationService } from '../../../shared/services/notification.service';
+import { TelemetryNotificationService } from '../../../shared/services/telemetry-notification.service';
import { TextToDownloadService } from '../../../shared/services/text-to-download.service';
@Component({
private router: Router,
private telemetryService: TelemetryService,
private i18n: I18n,
- private textToDownloadService: TextToDownloadService
+ private textToDownloadService: TextToDownloadService,
+ private telemetryNotificationService: TelemetryNotificationService
) {
super();
}
disableModule(message: string = null, followUpFunc: Function = null) {
this.telemetryService.enable(false).subscribe(() => {
+ this.telemetryNotificationService.setVisibility(true);
if (message) {
this.notificationService.show(NotificationType.success, message);
}
onSubmit() {
this.telemetryService.enable().subscribe(() => {
+ this.telemetryNotificationService.setVisibility(false);
this.notificationService.show(
NotificationType.success,
this.i18n('The Telemetry module has been configured and activated successfully.')
<cd-pwd-expiration-notification></cd-pwd-expiration-notification>
+<cd-telemetry-notification></cd-telemetry-notification>
<cd-notifications-sidebar></cd-notifications-sidebar>
<div class="cd-navbar-top">
provide: AuthStorageService,
useValue: {
getPermissions: jest.fn(),
- isPwdDisplayed$: { subscribe: jest.fn() }
+ isPwdDisplayed$: { subscribe: jest.fn() },
+ telemetryNotification$: { subscribe: jest.fn() }
}
},
{ provide: SummaryService, useValue: { subscribe: jest.fn() } },
} from '../../../shared/services/feature-toggles.service';
import { PrometheusAlertService } from '../../../shared/services/prometheus-alert.service';
import { SummaryService } from '../../../shared/services/summary.service';
+import { TelemetryNotificationService } from '../../../shared/services/telemetry-notification.service';
@Component({
selector: 'cd-navigation',
private authStorageService: AuthStorageService,
private summaryService: SummaryService,
private featureToggles: FeatureTogglesService,
+ private telemetryNotificationService: TelemetryNotificationService,
public prometheusAlertService: PrometheusAlertService
) {
this.permissions = this.authStorageService.getPermissions();
this.showTopNotification('isPwdDisplayed', isDisplayed);
})
);
+ this.subs.add(
+ this.telemetryNotificationService.update.subscribe((visible: boolean) => {
+ this.showTopNotification('telemetryNotificationEnabled', visible);
+ })
+ );
}
ngOnDestroy(): void {
import { SelectComponent } from './select/select.component';
import { SparklineComponent } from './sparkline/sparkline.component';
import { SubmitButtonComponent } from './submit-button/submit-button.component';
+import { TelemetryNotificationComponent } from './telemetry-notification/telemetry-notification.component';
import { UsageBarComponent } from './usage-bar/usage-bar.component';
import { ViewCacheComponent } from './view-cache/view-cache.component';
AlertPanelComponent,
FormModalComponent,
PwdExpirationNotificationComponent,
+ TelemetryNotificationComponent,
OrchestratorDocPanelComponent,
OrchestratorDocModalComponent
],
ConfigOptionComponent,
AlertPanelComponent,
PwdExpirationNotificationComponent,
+ TelemetryNotificationComponent,
OrchestratorDocPanelComponent
]
})
--- /dev/null
+<ngb-alert class="no-margin-bottom"
+ type="warning"
+ *ngIf="displayNotification"
+ (close)="close($event)">
+ <div i18n>The Telemetry module is not submitting telemetry data at the
+ moment. Click
+ <a routerLink="/telemetry"
+ class="alert-link">here</a> to activate it now.</div>
+</ngb-alert>
--- /dev/null
+::ng-deep .no-margin-bottom {
+ margin-bottom: 0;
+}
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NgbAlertModule } from '@ng-bootstrap/ng-bootstrap';
+import { ToastrModule } from 'ngx-toastr';
+import { of } from 'rxjs';
+
+import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
+import { UserFormModel } from '../../../core/auth/user-form/user-form.model';
+import { MgrModuleService } from '../../api/mgr-module.service';
+import { UserService } from '../../api/user.service';
+import { PipesModule } from '../../pipes/pipes.module';
+import { AuthStorageService } from '../../services/auth-storage.service';
+import { NotificationService } from '../../services/notification.service';
+import { TelemetryNotificationService } from '../../services/telemetry-notification.service';
+import { TelemetryNotificationComponent } from './telemetry-notification.component';
+
+describe('TelemetryActivationNotificationComponent', () => {
+ let component: TelemetryNotificationComponent;
+ let fixture: ComponentFixture<TelemetryNotificationComponent>;
+
+ let authStorageService: AuthStorageService;
+ let userService: UserService;
+ let mgrModuleService: MgrModuleService;
+ let notificationService: NotificationService;
+
+ let isNotificationHiddenSpy: jasmine.Spy;
+ let getUsernameSpy: jasmine.Spy;
+ let userServiceGetSpy: jasmine.Spy;
+ let getConfigSpy: jasmine.Spy;
+
+ const user: UserFormModel = {
+ username: 'username',
+ password: undefined,
+ name: 'User 1',
+ email: 'user1@email.com',
+ roles: ['read-only'],
+ enabled: true,
+ pwdExpirationDate: undefined,
+ pwdUpdateRequired: true
+ };
+ const admin: UserFormModel = {
+ username: 'admin',
+ password: undefined,
+ name: 'User 1',
+ email: 'user1@email.com',
+ roles: ['administrator'],
+ enabled: true,
+ pwdExpirationDate: undefined,
+ pwdUpdateRequired: true
+ };
+ const telemetryEnabledConfig = {
+ enabled: true
+ };
+ const telemetryDisabledConfig = {
+ enabled: false
+ };
+
+ configureTestBed({
+ declarations: [TelemetryNotificationComponent],
+ imports: [NgbAlertModule, HttpClientTestingModule, ToastrModule.forRoot(), PipesModule],
+ providers: [MgrModuleService, UserService, i18nProviders]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TelemetryNotificationComponent);
+ component = fixture.componentInstance;
+ authStorageService = TestBed.inject(AuthStorageService);
+ userService = TestBed.inject(UserService);
+ mgrModuleService = TestBed.inject(MgrModuleService);
+ notificationService = TestBed.inject(NotificationService);
+
+ isNotificationHiddenSpy = spyOn(component, 'isNotificationHidden').and.returnValue(false);
+ getUsernameSpy = spyOn(authStorageService, 'getUsername').and.returnValue('username');
+ userServiceGetSpy = spyOn(userService, 'get').and.returnValue(of(admin)); // Not the best name but it sounded better than `getSpy`
+ getConfigSpy = spyOn(mgrModuleService, 'getConfig').and.returnValue(
+ of(telemetryDisabledConfig)
+ );
+ });
+
+ it('should create', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ it('should not show notification again if the user closed it before', () => {
+ isNotificationHiddenSpy.and.returnValue(true);
+ fixture.detectChanges();
+ expect(component.displayNotification).toBe(false);
+ });
+
+ it('should not show notification for an user without administrator role', () => {
+ userServiceGetSpy.and.returnValue(of(user));
+ fixture.detectChanges();
+ expect(component.displayNotification).toBe(false);
+ });
+
+ it('should not show notification if the module is enabled already', () => {
+ getUsernameSpy.and.returnValue('admin');
+ getConfigSpy.and.returnValue(of(telemetryEnabledConfig));
+ fixture.detectChanges();
+ expect(component.displayNotification).toBe(false);
+ });
+
+ it('should show the notification if all pre-conditions set accordingly', () => {
+ fixture.detectChanges();
+ expect(component.displayNotification).toBe(true);
+ });
+
+ it('should hide the notification if the user closes it', () => {
+ spyOn(notificationService, 'show');
+ fixture.detectChanges();
+ component.close();
+ expect(notificationService.show).toHaveBeenCalled();
+ expect(localStorage.getItem('telemetry_notification_hidden')).toBe('true');
+ });
+
+ it('should hide the notification if the user logs out', () => {
+ const telemetryNotificationService = TestBed.inject(TelemetryNotificationService);
+ spyOn(telemetryNotificationService, 'setVisibility');
+ fixture.detectChanges();
+ component.ngOnDestroy();
+ expect(telemetryNotificationService.setVisibility).toHaveBeenCalledWith(false);
+ });
+});
--- /dev/null
+import { Component, OnDestroy, OnInit } from '@angular/core';
+
+import { I18n } from '@ngx-translate/i18n-polyfill';
+
+import { UserFormModel } from '../../../core/auth/user-form/user-form.model';
+import { MgrModuleService } from '../../api/mgr-module.service';
+import { UserService } from '../../api/user.service';
+import { NotificationType } from '../../enum/notification-type.enum';
+import { AuthStorageService } from '../../services/auth-storage.service';
+import { NotificationService } from '../../services/notification.service';
+import { TelemetryNotificationService } from '../../services/telemetry-notification.service';
+
+@Component({
+ selector: 'cd-telemetry-notification',
+ templateUrl: './telemetry-notification.component.html',
+ styleUrls: ['./telemetry-notification.component.scss']
+})
+export class TelemetryNotificationComponent implements OnInit, OnDestroy {
+ displayNotification = false;
+
+ constructor(
+ private mgrModuleService: MgrModuleService,
+ private authStorageService: AuthStorageService,
+ private userService: UserService,
+ private notificationService: NotificationService,
+ private telemetryNotificationService: TelemetryNotificationService,
+ private i18n: I18n
+ ) {}
+
+ ngOnInit() {
+ this.telemetryNotificationService.update.subscribe((visible: boolean) => {
+ this.displayNotification = visible;
+ });
+
+ if (!this.isNotificationHidden()) {
+ const username = this.authStorageService.getUsername();
+ this.userService.get(username).subscribe((user: UserFormModel) => {
+ if (user.roles.includes('administrator')) {
+ this.mgrModuleService.getConfig('telemetry').subscribe((options) => {
+ if (!options['enabled']) {
+ this.telemetryNotificationService.setVisibility(true);
+ }
+ });
+ }
+ });
+ }
+ }
+
+ ngOnDestroy() {
+ this.telemetryNotificationService.setVisibility(false);
+ }
+
+ isNotificationHidden(): boolean {
+ return localStorage.getItem('telemetry_notification_hidden') === 'true';
+ }
+
+ close() {
+ this.telemetryNotificationService.setVisibility(false);
+ localStorage.setItem('telemetry_notification_hidden', 'true');
+ this.notificationService.show(
+ NotificationType.success,
+ this.i18n('Telemetry activation reminder muted'),
+ this.i18n(
+ 'You can activate the module on the Telemetry configuration ' +
+ 'page (<b>Dashboard Settings</b> -> <b>Telemetry configuration</b>) at any time.'
+ )
+ );
+ }
+}
--- /dev/null
+import { TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '../../../testing/unit-test-helper';
+import { TelemetryNotificationService } from './telemetry-notification.service';
+
+describe('TelemetryNotificationService', () => {
+ let service: TelemetryNotificationService;
+
+ configureTestBed({
+ providers: [TelemetryNotificationService]
+ });
+
+ beforeEach(() => {
+ service = TestBed.inject(TelemetryNotificationService);
+ spyOn(service.update, 'emit');
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+
+ it('should set notification visibility to true', () => {
+ service.setVisibility(true);
+ expect(service.visible).toBe(true);
+ expect(service.update.emit).toHaveBeenCalledWith(true);
+ });
+
+ it('should set notification visibility to false', () => {
+ service.setVisibility(false);
+ expect(service.visible).toBe(false);
+ expect(service.update.emit).toHaveBeenCalledWith(false);
+ });
+});
--- /dev/null
+import { EventEmitter, Injectable, Output } from '@angular/core';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TelemetryNotificationService {
+ visible = false;
+
+ @Output()
+ update: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+ setVisibility(visible: boolean) {
+ this.visible = visible;
+ this.update.emit(visible);
+ }
+}