]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: move monitoring tabs to a single page
authorVolker Theile <vtheile@suse.com>
Thu, 2 Apr 2020 10:25:28 +0000 (12:25 +0200)
committerVolker Theile <vtheile@suse.com>
Thu, 2 Apr 2020 11:18:50 +0000 (13:18 +0200)
with a tab for 'active alerts', 'all alerts' and 'silences'. Due to
ambiguity with existing names, `AlertListComponent` has been renamed to
`ActiveAlertListComponent`. Introduces `MonitoringListComponent` as
first page for monitoring concerns, using path `/monitoring`.

Keeps the activated tab open, independent of the way that's used to go
back to the previous page, be it the cancel button or submit button or
the link on the breadcrumb. Also keeps the active tab open even when the
page is reloaded.

Fixes: https://tracker.ceph.com/issues/42877
Signed-off-by: Patrick Seidensal <pseidensal@suse.com>
(cherry picked from commit 855f214b29c8ed935c8f4ba0b8a8396692f946a1)

Conflicts:
  src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts
  src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts
  src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
  src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
  src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts
  src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts
  src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html
  src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts
  src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
  src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts
  src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html

30 files changed:
src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/app-routing.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/cluster.module.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts [deleted file]
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-list/silence-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/breadcrumbs/breadcrumbs.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-actions/table-actions.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/models/cd-table-action.ts

diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.e2e-spec.ts
deleted file mode 100644 (file)
index 2f438f8..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-import { Helper } from '../helper.po';
-import { AlertsPage } from './alerts.po';
-
-describe('Alerts page', () => {
-  let page: AlertsPage;
-
-  beforeAll(() => {
-    page = new AlertsPage();
-  });
-
-  afterEach(() => {
-    Helper.checkConsole();
-  });
-
-  describe('breadcrumb test', () => {
-    beforeAll(() => {
-      page.navigateTo();
-    });
-
-    it('should open and show breadcrumb', () => {
-      Helper.waitTextToBePresent(Helper.getBreadcrumb(), 'Alerts');
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts b/src/pybind/mgr/dashboard/frontend/e2e/cluster/alerts.po.ts
deleted file mode 100644 (file)
index 076b15b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-import { browser } from 'protractor';
-
-export class AlertsPage {
-  navigateTo() {
-    return browser.get('/#/alerts');
-  }
-}
index 73dbc4088f50f11d144a6c982c3000a7486ccf30..4553ce8e58dd119f9c6c30d50df3b90ca9eaae56 100644 (file)
@@ -13,9 +13,8 @@ import { MgrModuleFormComponent } from './ceph/cluster/mgr-modules/mgr-module-fo
 import { MgrModuleListComponent } from './ceph/cluster/mgr-modules/mgr-module-list/mgr-module-list.component';
 import { MonitorComponent } from './ceph/cluster/monitor/monitor.component';
 import { OsdListComponent } from './ceph/cluster/osd/osd-list/osd-list.component';
-import { AlertListComponent } from './ceph/cluster/prometheus/alert-list/alert-list.component';
+import { MonitoringListComponent } from './ceph/cluster/prometheus/monitoring-list/monitoring-list.component';
 import { SilenceFormComponent } from './ceph/cluster/prometheus/silence-form/silence-form.component';
-import { SilenceListComponent } from './ceph/cluster/prometheus/silence-list/silence-list.component';
 import { DashboardComponent } from './ceph/dashboard/dashboard/dashboard.component';
 import { Nfs501Component } from './ceph/nfs/nfs-501/nfs-501.component';
 import { NfsFormComponent } from './ceph/nfs/nfs-form/nfs-form.component';
@@ -110,34 +109,31 @@ const routes: Routes = [
     data: { breadcrumbs: 'Cluster/Logs' }
   },
   {
-    path: 'alerts',
-    component: AlertListComponent,
+    path: 'monitoring',
     canActivate: [AuthGuardService],
-    data: { breadcrumbs: 'Cluster/Alerts' }
-  },
-  {
-    path: 'silence',
-    canActivate: [AuthGuardService],
-    data: { breadcrumbs: 'Cluster/Silences' },
+    data: { breadcrumbs: 'Cluster/Monitoring' },
     children: [
-      { path: '', component: SilenceListComponent },
       {
-        path: URLVerbs.CREATE,
+        path: '',
+        component: MonitoringListComponent
+      },
+      {
+        path: 'silence/' + URLVerbs.CREATE,
         component: SilenceFormComponent,
-        data: { breadcrumbs: ActionLabels.CREATE }
+        data: { breadcrumbs: `${ActionLabels.CREATE} Silence` }
       },
       {
-        path: `${URLVerbs.CREATE}/:id`,
+        path: `silence/${URLVerbs.CREATE}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.CREATE }
       },
       {
-        path: `${URLVerbs.EDIT}/:id`,
+        path: `silence/${URLVerbs.EDIT}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.EDIT }
       },
       {
-        path: `${URLVerbs.RECREATE}/:id`,
+        path: `silence/${URLVerbs.RECREATE}/:id`,
         component: SilenceFormComponent,
         data: { breadcrumbs: ActionLabels.RECREATE }
       }
index ab0d71f02ee02b0b5b9a1070e518ca4fe73badb3..11330561573fe43bd250bc9177a077fe11530961 100644 (file)
@@ -32,8 +32,8 @@ import { OsdPgScrubModalComponent } from './osd/osd-pg-scrub-modal/osd-pg-scrub-
 import { OsdRecvSpeedModalComponent } from './osd/osd-recv-speed-modal/osd-recv-speed-modal.component';
 import { OsdReweightModalComponent } from './osd/osd-reweight-modal/osd-reweight-modal.component';
 import { OsdScrubModalComponent } from './osd/osd-scrub-modal/osd-scrub-modal.component';
-import { AlertListComponent } from './prometheus/alert-list/alert-list.component';
-import { PrometheusTabsComponent } from './prometheus/prometheus-tabs/prometheus-tabs.component';
+import { ActiveAlertListComponent } from './prometheus/active-alert-list/active-alert-list.component';
+import { MonitoringListComponent } from './prometheus/monitoring-list/monitoring-list.component';
 import { RulesListComponent } from './prometheus/rules-list/rules-list.component';
 import { SilenceFormComponent } from './prometheus/silence-form/silence-form.component';
 import { SilenceListComponent } from './prometheus/silence-list/silence-list.component';
@@ -86,13 +86,13 @@ import { SilenceMatcherModalComponent } from './prometheus/silence-matcher-modal
     LogsComponent,
     OsdRecvSpeedModalComponent,
     OsdPgScrubModalComponent,
-    AlertListComponent,
+    ActiveAlertListComponent,
     OsdRecvSpeedModalComponent,
     SilenceFormComponent,
     SilenceListComponent,
-    PrometheusTabsComponent,
     SilenceMatcherModalComponent,
-    RulesListComponent
+    RulesListComponent,
+    MonitoringListComponent
   ]
 })
 export class ClusterModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html
new file mode 100644 (file)
index 0000000..a5b11ee
--- /dev/null
@@ -0,0 +1,32 @@
+<cd-table [data]="prometheusAlertService.alerts"
+          [columns]="columns"
+          identifier="fingerprint"
+          [forceIdentifier]="true"
+          [customCss]="customCss"
+          selectionType="single"
+          (updateSelection)="updateSelection($event)">
+  <cd-table-actions class="table-actions"
+                    [permission]="permission"
+                    [selection]="selection"
+                    [tableActions]="tableActions">
+  </cd-table-actions>
+  <tabset cdTableDetail *ngIf="selection.hasSingleSelection">
+    <tab i18n-heading
+         heading="Details">
+      <cd-table-key-value [renderObjects]="true"
+                          [hideEmpty]="true"
+                          [appendParentKey]="false"
+                          [data]="selection.first()"
+                          [customCss]="customCss"
+                          [autoReload]="false">
+      </cd-table-key-value>
+    </tab>
+  </tabset>
+</cd-table>
+
+<ng-template #externalLinkTpl
+             let-row="row"
+             let-value="value">
+  <a [href]="value" target="_blank"><i class="fa fa-line-chart"></i> Source</a>
+</ng-template>
+
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.spec.ts
new file mode 100644 (file)
index 0000000..85eb89f
--- /dev/null
@@ -0,0 +1,132 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { TabsModule } from 'ngx-bootstrap/tabs';
+import { ToastrModule } from 'ngx-toastr';
+
+import {
+  configureTestBed,
+  i18nProviders,
+  PermissionHelper
+} from '../../../../../testing/unit-test-helper';
+import { CoreModule } from '../../../../core/core.module';
+import { TableActionsComponent } from '../../../../shared/datatable/table-actions/table-actions.component';
+import { SharedModule } from '../../../../shared/shared.module';
+import { CephModule } from '../../../ceph.module';
+import { DashboardModule } from '../../../dashboard/dashboard.module';
+import { ClusterModule } from '../../cluster.module';
+import { ActiveAlertListComponent } from './active-alert-list.component';
+
+describe('ActiveAlertListComponent', () => {
+  let component: ActiveAlertListComponent;
+  let fixture: ComponentFixture<ActiveAlertListComponent>;
+
+  configureTestBed({
+    imports: [
+      HttpClientTestingModule,
+      TabsModule.forRoot(),
+      RouterTestingModule,
+      ToastrModule.forRoot(),
+      SharedModule,
+      ClusterModule,
+      DashboardModule,
+      CephModule,
+      CoreModule
+    ],
+    declarations: [],
+    providers: [i18nProviders]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ActiveAlertListComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should create', () => {
+    fixture.detectChanges();
+    expect(component).toBeTruthy();
+  });
+
+  describe('show action buttons and drop down actions depending on permissions', () => {
+    let tableActions: TableActionsComponent;
+    let scenario: { fn; empty; single };
+    let permissionHelper: PermissionHelper;
+    let combinations: number[][];
+
+    const getTableActionComponent = (): TableActionsComponent => {
+      fixture.detectChanges();
+      return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
+    };
+
+    beforeEach(() => {
+      permissionHelper = new PermissionHelper(component.permission, () =>
+        getTableActionComponent()
+      );
+      scenario = {
+        fn: () => tableActions.getCurrentButton().name,
+        single: 'Create Silence',
+        empty: 'Create Silence'
+      };
+      tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
+    });
+
+    const permissionSwitch = (combination) => {
+      tableActions = permissionHelper.setPermissionsAndGetActions(
+        combination[0],
+        combination[1],
+        combination[2]
+      );
+      tableActions.tableActions = component.tableActions;
+      tableActions.ngOnInit();
+    };
+
+    const testCombinations = (test: Function) => {
+      combinations.forEach((combination) => {
+        permissionSwitch(combination);
+        test();
+      });
+    };
+
+    describe('with every permission combination that includes create', () => {
+      beforeEach(() => {
+        combinations = [[1, 1, 1], [1, 1, 0], [1, 0, 1], [1, 0, 0]];
+      });
+
+      it(`always shows 'Create silence' as main action`, () => {
+        testCombinations(() => permissionHelper.testScenarios(scenario));
+      });
+
+      it('shows all actions', () => {
+        testCombinations(() => {
+          expect(tableActions.tableActions.length).toBe(1);
+          expect(tableActions.tableActions).toEqual(component.tableActions);
+        });
+      });
+    });
+
+    describe('with every permission combination that does not include create', () => {
+      beforeEach(() => {
+        combinations = [[0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]];
+      });
+
+      it(`won't show any action`, () => {
+        testCombinations(() => {
+          permissionHelper.testScenarios({
+            fn: () => tableActions.getCurrentButton(),
+            single: undefined,
+            empty: undefined
+          });
+        });
+      });
+
+      it('shows no actions', () => {
+        testCombinations(() => {
+          expect(tableActions.tableActions.length).toBe(0);
+          expect(tableActions.tableActions).toEqual([]);
+        });
+      });
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.ts
new file mode 100644 (file)
index 0000000..a61dae4
--- /dev/null
@@ -0,0 +1,97 @@
+import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
+import { I18n } from '@ngx-translate/i18n-polyfill';
+import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
+import { Icons } from '../../../../shared/enum/icons.enum';
+import { CdTableAction } from '../../../../shared/models/cd-table-action';
+import { CdTableColumn } from '../../../../shared/models/cd-table-column';
+import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
+import { Permission } from '../../../../shared/models/permissions';
+import { CdDatePipe } from '../../../../shared/pipes/cd-date.pipe';
+import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
+import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
+import { URLBuilderService } from '../../../../shared/services/url-builder.service';
+
+const BASE_URL = 'silence'; // as only silence actions can be used
+
+@Component({
+  selector: 'cd-active-alert-list',
+  providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
+  templateUrl: './active-alert-list.component.html',
+  styleUrls: ['./active-alert-list.component.scss']
+})
+export class ActiveAlertListComponent implements OnInit {
+  @ViewChild('externalLinkTpl')
+  externalLinkTpl: TemplateRef<any>;
+  columns: CdTableColumn[];
+  tableActions: CdTableAction[];
+  permission: Permission;
+  selection = new CdTableSelection();
+  icons = Icons;
+  customCss = {
+    'label label-danger': 'active',
+    'label label-warning': 'unprocessed',
+    'label label-info': 'suppressed'
+  };
+
+  constructor(
+    // NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
+    private authStorageService: AuthStorageService,
+    public prometheusAlertService: PrometheusAlertService,
+    private urlBuilder: URLBuilderService,
+    private i18n: I18n,
+    private cdDatePipe: CdDatePipe
+  ) {
+    this.permission = this.authStorageService.getPermissions().prometheus;
+    this.tableActions = [
+      {
+        permission: 'create',
+        canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
+        disable: (selection: CdTableSelection) =>
+          !selection.hasSingleSelection || selection.first().cdExecuting,
+        icon: Icons.add,
+        routerLink: () =>
+          '/monitoring' + this.urlBuilder.getCreateFrom(this.selection.first().fingerprint),
+        name: this.i18n('Create Silence')
+      }
+    ];
+  }
+
+  ngOnInit() {
+    this.columns = [
+      {
+        name: this.i18n('Name'),
+        prop: 'labels.alertname',
+        flexGrow: 2
+      },
+      {
+        name: this.i18n('Job'),
+        prop: 'labels.job',
+        flexGrow: 2
+      },
+      {
+        name: this.i18n('Severity'),
+        prop: 'labels.severity'
+      },
+      {
+        name: this.i18n('State'),
+        prop: 'status.state',
+        cellTransformation: CellTemplate.classAdding
+      },
+      {
+        name: this.i18n('Started'),
+        prop: 'startsAt',
+        pipe: this.cdDatePipe
+      },
+      {
+        name: this.i18n('URL'),
+        prop: 'generatorURL',
+        sortable: false,
+        cellTemplate: this.externalLinkTpl
+      }
+    ];
+  }
+
+  updateSelection(selection: CdTableSelection) {
+    this.selection = selection;
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.html
deleted file mode 100644 (file)
index 90b1bf9..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-<cd-prometheus-tabs></cd-prometheus-tabs>
-
-<h3 class="cd-header"
-    i18n>All Alerts</h3>
-<cd-rules-list [data]="prometheusAlertService.rules"></cd-rules-list>
-
-<h3 class="cd-header"
-    i18n>Active Alerts</h3>
-<cd-table [data]="prometheusAlertService.alerts"
-          [columns]="columns"
-          identifier="fingerprint"
-          [forceIdentifier]="true"
-          [customCss]="customCss"
-          selectionType="single"
-          (updateSelection)="updateSelection($event)">
-  <cd-table-actions class="table-actions"
-                    [permission]="permission"
-                    [selection]="selection"
-                    [tableActions]="tableActions">
-  </cd-table-actions>
-  <tabset cdTableDetail *ngIf="selection.hasSingleSelection">
-    <tab i18n-heading
-         heading="Details">
-      <cd-table-key-value [renderObjects]="true"
-                          [hideEmpty]="true"
-                          [appendParentKey]="false"
-                          [data]="selection.first()"
-                          [customCss]="customCss"
-                          [autoReload]="false">
-      </cd-table-key-value>
-    </tab>
-  </tabset>
-</cd-table>
-
-<ng-template #externalLinkTpl
-             let-row="row"
-             let-value="value">
-  <a [href]="value" target="_blank"><i class="fa fa-line-chart"></i> Source</a>
-</ng-template>
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.spec.ts
deleted file mode 100644 (file)
index a684943..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { TabsModule } from 'ngx-bootstrap/tabs';
-import { ToastrModule } from 'ngx-toastr';
-
-import {
-  configureTestBed,
-  i18nProviders,
-  PermissionHelper
-} from '../../../../../testing/unit-test-helper';
-import { CoreModule } from '../../../../core/core.module';
-import { TableActionsComponent } from '../../../../shared/datatable/table-actions/table-actions.component';
-import { SharedModule } from '../../../../shared/shared.module';
-import { CephModule } from '../../../ceph.module';
-import { DashboardModule } from '../../../dashboard/dashboard.module';
-import { ClusterModule } from '../../cluster.module';
-import { AlertListComponent } from './alert-list.component';
-
-describe('PrometheusListComponent', () => {
-  let component: AlertListComponent;
-  let fixture: ComponentFixture<AlertListComponent>;
-
-  configureTestBed({
-    imports: [
-      HttpClientTestingModule,
-      TabsModule.forRoot(),
-      RouterTestingModule,
-      ToastrModule.forRoot(),
-      SharedModule,
-      ClusterModule,
-      DashboardModule,
-      CephModule,
-      CoreModule
-    ],
-    declarations: [],
-    providers: [i18nProviders]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(AlertListComponent);
-    component = fixture.componentInstance;
-  });
-
-  it('should create', () => {
-    fixture.detectChanges();
-    expect(component).toBeTruthy();
-  });
-
-  describe('show action buttons and drop down actions depending on permissions', () => {
-    let tableActions: TableActionsComponent;
-    let scenario: { fn; empty; single };
-    let permissionHelper: PermissionHelper;
-    let combinations: number[][];
-
-    const getTableActionComponent = (): TableActionsComponent => {
-      fixture.detectChanges();
-      return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
-    };
-
-    beforeEach(() => {
-      permissionHelper = new PermissionHelper(component.permission, () =>
-        getTableActionComponent()
-      );
-      scenario = {
-        fn: () => tableActions.getCurrentButton().name,
-        single: 'Create silence',
-        empty: 'Create silence'
-      };
-      tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
-    });
-
-    const permissionSwitch = (combination) => {
-      tableActions = permissionHelper.setPermissionsAndGetActions(
-        combination[0],
-        combination[1],
-        combination[2]
-      );
-      tableActions.tableActions = component.tableActions;
-      tableActions.ngOnInit();
-    };
-
-    const testCombinations = (test: Function) => {
-      combinations.forEach((combination) => {
-        permissionSwitch(combination);
-        test();
-      });
-    };
-
-    describe('with every permission combination that includes create', () => {
-      beforeEach(() => {
-        combinations = [[1, 1, 1], [1, 1, 0], [1, 0, 1], [1, 0, 0]];
-      });
-
-      it(`always shows 'Create silence' as main action`, () => {
-        testCombinations(() => permissionHelper.testScenarios(scenario));
-      });
-
-      it('shows all actions', () => {
-        testCombinations(() => {
-          expect(tableActions.tableActions.length).toBe(1);
-          expect(tableActions.tableActions).toEqual(component.tableActions);
-        });
-      });
-    });
-
-    describe('with every permission combination that does not include create', () => {
-      beforeEach(() => {
-        combinations = [[0, 1, 1], [0, 1, 0], [0, 0, 1], [0, 0, 0]];
-      });
-
-      it(`won't show any action`, () => {
-        testCombinations(() => {
-          permissionHelper.testScenarios({
-            fn: () => tableActions.getCurrentButton(),
-            single: undefined,
-            empty: undefined
-          });
-        });
-      });
-
-      it('shows no actions', () => {
-        testCombinations(() => {
-          expect(tableActions.tableActions.length).toBe(0);
-          expect(tableActions.tableActions).toEqual([]);
-        });
-      });
-    });
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/alert-list/alert-list.component.ts
deleted file mode 100644 (file)
index 927f3cf..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
-import { I18n } from '@ngx-translate/i18n-polyfill';
-import { CellTemplate } from '../../../../shared/enum/cell-template.enum';
-import { Icons } from '../../../../shared/enum/icons.enum';
-import { CdTableAction } from '../../../../shared/models/cd-table-action';
-import { CdTableColumn } from '../../../../shared/models/cd-table-column';
-import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
-import { Permission } from '../../../../shared/models/permissions';
-import { CdDatePipe } from '../../../../shared/pipes/cd-date.pipe';
-import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
-import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
-import { URLBuilderService } from '../../../../shared/services/url-builder.service';
-
-const BASE_URL = 'silence'; // as only silence actions can be used
-
-@Component({
-  selector: 'cd-prometheus-list',
-  providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
-  templateUrl: './alert-list.component.html',
-  styleUrls: ['./alert-list.component.scss']
-})
-export class AlertListComponent implements OnInit {
-  @ViewChild('externalLinkTpl')
-  externalLinkTpl: TemplateRef<any>;
-  columns: CdTableColumn[];
-  tableActions: CdTableAction[];
-  permission: Permission;
-  selection = new CdTableSelection();
-  icons = Icons;
-  customCss = {
-    'label label-danger': 'active',
-    'label label-warning': 'unprocessed',
-    'label label-info': 'suppressed'
-  };
-
-  constructor(
-    // NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
-    private authStorageService: AuthStorageService,
-    public prometheusAlertService: PrometheusAlertService,
-    private urlBuilder: URLBuilderService,
-    private i18n: I18n,
-    private cdDatePipe: CdDatePipe
-  ) {
-    this.permission = this.authStorageService.getPermissions().prometheus;
-    this.tableActions = [
-      {
-        permission: 'create',
-        canBePrimary: (selection: CdTableSelection) => selection.hasSingleSelection,
-        disable: (selection: CdTableSelection) =>
-          !selection.hasSingleSelection || selection.first().cdExecuting,
-        icon: Icons.add,
-        routerLink: () => this.urlBuilder.getCreateFrom(this.selection.first().fingerprint),
-        name: this.i18n('Create silence')
-      }
-    ];
-  }
-
-  ngOnInit() {
-    this.columns = [
-      {
-        name: this.i18n('Name'),
-        prop: 'labels.alertname',
-        flexGrow: 2
-      },
-      {
-        name: this.i18n('Job'),
-        prop: 'labels.job',
-        flexGrow: 2
-      },
-      {
-        name: this.i18n('Severity'),
-        prop: 'labels.severity'
-      },
-      {
-        name: this.i18n('State'),
-        prop: 'status.state',
-        cellTransformation: CellTemplate.classAdding
-      },
-      {
-        name: this.i18n('Started'),
-        prop: 'startsAt',
-        pipe: this.cdDatePipe
-      },
-      {
-        name: this.i18n('URL'),
-        prop: 'generatorURL',
-        sortable: false,
-        cellTemplate: this.externalLinkTpl
-      }
-    ];
-  }
-
-  updateSelection(selection: CdTableSelection) {
-    this.selection = selection;
-  }
-}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.html
new file mode 100644 (file)
index 0000000..55f571d
--- /dev/null
@@ -0,0 +1,20 @@
+<tabset #tabs>
+  <tab id="active-alerts"
+       heading="Active Alerts"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-active-alert-list></cd-active-alert-list>
+  </tab>
+  <tab id="all-alerts"
+       heading="All Alerts"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-rules-list [data]="prometheusAlertService.rules"></cd-rules-list>
+  </tab>
+  <tab id="silences"
+       heading="Silences"
+       (selectTab)="setFragment($event)"
+       i18n-heading>
+    <cd-silences-list></cd-silences-list>
+  </tab>
+</tabset>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.spec.ts
new file mode 100644 (file)
index 0000000..bf0bf6c
--- /dev/null
@@ -0,0 +1,46 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+
+import { ToastrModule } from 'ngx-toastr';
+
+import { i18nProviders } from '../../../../../testing/unit-test-helper';
+import { AuthModule } from '../../../../core/auth/auth.module';
+import { CoreModule } from '../../../../core/core.module';
+import { CephfsModule } from '../../../cephfs/cephfs.module';
+import { DashboardModule } from '../../../dashboard/dashboard.module';
+import { NfsModule } from '../../../nfs/nfs.module';
+import { ClusterModule } from '../../cluster.module';
+import { MonitoringListComponent } from './monitoring-list.component';
+
+describe('MonitoringListComponent', () => {
+  let component: MonitoringListComponent;
+  let fixture: ComponentFixture<MonitoringListComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      imports: [
+        ClusterModule,
+        DashboardModule,
+        CephfsModule,
+        AuthModule,
+        NfsModule,
+        CoreModule,
+        ToastrModule.forRoot(),
+        HttpClientTestingModule
+      ],
+      declarations: [],
+      providers: [i18nProviders]
+    }).compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MonitoringListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/monitoring-list/monitoring-list.component.ts
new file mode 100644 (file)
index 0000000..efc3019
--- /dev/null
@@ -0,0 +1,40 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { TabDirective, TabsetComponent } from 'ngx-bootstrap/tabs';
+
+import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
+
+@Component({
+  selector: 'cd-monitoring-list',
+  templateUrl: './monitoring-list.component.html',
+  styleUrls: ['./monitoring-list.component.scss']
+})
+export class MonitoringListComponent implements OnInit {
+  @ViewChild('tabs')
+  tabs: TabsetComponent;
+
+  constructor(
+    public prometheusAlertService: PrometheusAlertService,
+    private route: ActivatedRoute,
+    private router: Router
+  ) {}
+
+  ngOnInit() {
+    // Activate tab according to given fragment
+    if (this.route.snapshot.fragment) {
+      const tab = this.tabs.tabs.find(
+        (t) => t.elementRef.nativeElement.id === this.route.snapshot.fragment
+      );
+      if (tab) {
+        tab.active = true;
+      }
+      // Ensure fragment is not removed, so page can always be reloaded with the same tab open.
+      this.router.navigate([], { fragment: this.route.snapshot.fragment });
+    }
+  }
+
+  setFragment(element: TabDirective) {
+    this.router.navigate([], { fragment: element.id });
+  }
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.html
deleted file mode 100644 (file)
index b2f530a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<tabset>
-  <tab heading="Alerts"
-       i18n-heading
-       [active]="url === '/alerts'"
-       (select)="navigateTo('/alerts')">
-  </tab>
-  <tab heading="Silences"
-       i18n-heading
-       [active]="url === '/silence'"
-       (select)="navigateTo('/silence')">
-  </tab>
-</tabset>
-
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.scss
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.spec.ts
deleted file mode 100644 (file)
index 65cc58d..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-import { HttpClientTestingModule } from '@angular/common/http/testing';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { By } from '@angular/platform-browser';
-import { Router } from '@angular/router';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { TabsModule } from 'ngx-bootstrap/tabs';
-
-import { configureTestBed } from '../../../../../testing/unit-test-helper';
-import { PrometheusTabsComponent } from './prometheus-tabs.component';
-
-describe('PrometheusTabsComponent', () => {
-  let component: PrometheusTabsComponent;
-  let fixture: ComponentFixture<PrometheusTabsComponent>;
-  let router: Router;
-
-  const selectTab = (index) => {
-    fixture.debugElement.queryAll(By.css('tab'))[index].triggerEventHandler('select', null);
-  };
-
-  configureTestBed({
-    declarations: [PrometheusTabsComponent],
-    imports: [RouterTestingModule, HttpClientTestingModule, TabsModule.forRoot()]
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(PrometheusTabsComponent);
-    component = fixture.componentInstance;
-    router = TestBed.get(Router);
-    spyOn(router, 'navigate').and.stub();
-    fixture.detectChanges();
-  });
-
-  it('should create', () => {
-    expect(component).toBeTruthy();
-  });
-
-  it('should redirect to alert listing', () => {
-    selectTab(0);
-    expect(router.navigate).toHaveBeenCalledWith(['/alerts']);
-  });
-
-  it('should redirect to silence listing', () => {
-    selectTab(1);
-    expect(router.navigate).toHaveBeenCalledWith(['/silence']);
-  });
-});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/prometheus-tabs/prometheus-tabs.component.ts
deleted file mode 100644 (file)
index 5675eb7..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Component } from '@angular/core';
-import { Router } from '@angular/router';
-
-@Component({
-  selector: 'cd-prometheus-tabs',
-  templateUrl: './prometheus-tabs.component.html',
-  styleUrls: ['./prometheus-tabs.component.scss']
-})
-export class PrometheusTabsComponent {
-  url: string;
-
-  constructor(private router: Router) {
-    this.url = this.router.url;
-  }
-
-  navigateTo(url) {
-    this.router.navigate([url]);
-  }
-}
index cd338b4355d7bc6948f62354eb70ee5fefef7479..73d5fd8a620ca9976aeed15a8f21cc5f0bfc636d 100644 (file)
@@ -45,7 +45,7 @@ describe('SilenceFormComponent', () => {
   let ifPrometheusSpy;
   // Helper
   let prometheus: PrometheusHelper;
-  let formH: FormHelper;
+  let formHelper: FormHelper;
   let fixtureH: FixtureHelper;
   let params;
   // Date mocking related
@@ -86,10 +86,10 @@ describe('SilenceFormComponent', () => {
 
   const changeAction = (action: string) => {
     const modes = {
-      add: '/silence/add',
-      alertAdd: '/silence/add/someAlert',
-      recreate: '/silence/recreate/someExpiredId',
-      edit: '/silence/edit/someNotExpiredId'
+      add: '/monitoring/silence/add',
+      alertAdd: '/monitoring/silence/add/someAlert',
+      recreate: '/monitoring/silence/recreate/someExpiredId',
+      edit: '/monitoring/silence/edit/someNotExpiredId'
     };
     Object.defineProperty(router, 'url', { value: modes[action] });
     callInit();
@@ -138,7 +138,7 @@ describe('SilenceFormComponent', () => {
     fixtureH = new FixtureHelper(fixture);
     component = fixture.componentInstance;
     form = component.form;
-    formH = new FormHelper(form);
+    formHelper = new FormHelper(form);
     fixture.detectChanges();
   });
 
@@ -333,7 +333,7 @@ describe('SilenceFormComponent', () => {
 
       it('should raise invalid start date error', fakeAsync(() => {
         changeStartDate('No valid date');
-        formH.expectError('startsAt', 'bsDate');
+        formHelper.expectError('startsAt', 'bsDate');
         expect(form.getValue('startsAt').toString()).toBe('Invalid Date');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-22T02:00:00'));
       }));
@@ -341,9 +341,9 @@ describe('SilenceFormComponent', () => {
 
     describe('on duration change', () => {
       it('changes end date if duration is changed', () => {
-        formH.setValue('duration', '15m');
+        formHelper.setValue('duration', '15m');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-22T00:15'));
-        formH.setValue('duration', '5d 23h');
+        formHelper.setValue('duration', '5d 23h');
         expect(form.getValue('endsAt')).toEqual(new Date('2022-02-27T23:00'));
       });
     });
@@ -363,7 +363,7 @@ describe('SilenceFormComponent', () => {
 
       it('should raise invalid end date error', fakeAsync(() => {
         changeEndDate('No valid date');
-        formH.expectError('endsAt', 'bsDate');
+        formHelper.expectError('endsAt', 'bsDate');
         expect(form.getValue('endsAt').toString()).toBe('Invalid Date');
         expect(form.getValue('startsAt')).toEqual(baseTime);
       }));
@@ -371,20 +371,20 @@ describe('SilenceFormComponent', () => {
   });
 
   it('should have a creator field', () => {
-    formH.expectValid('createdBy');
-    formH.expectErrorChange('createdBy', '', 'required');
-    formH.expectValidChange('createdBy', 'Mighty FSM');
+    formHelper.expectValid('createdBy');
+    formHelper.expectErrorChange('createdBy', '', 'required');
+    formHelper.expectValidChange('createdBy', 'Mighty FSM');
   });
 
   it('should have a comment field', () => {
-    formH.expectError('comment', 'required');
-    formH.expectValidChange('comment', 'A pretty long comment');
+    formHelper.expectError('comment', 'required');
+    formHelper.expectValidChange('comment', 'A pretty long comment');
   });
 
   it('should be a valid form if all inputs are filled and at least one matcher was added', () => {
     expect(form.valid).toBeFalsy();
-    formH.expectValidChange('createdBy', 'Mighty FSM');
-    formH.expectValidChange('comment', 'A pretty long comment');
+    formHelper.expectValidChange('createdBy', 'Mighty FSM');
+    formHelper.expectValidChange('comment', 'A pretty long comment');
     addMatcher('job', 'someJob', false);
     expect(form.valid).toBeTruthy();
   });
@@ -525,7 +525,7 @@ describe('SilenceFormComponent', () => {
 
     const fillAndSubmit = () => {
       ['createdBy', 'comment'].forEach((attr) => {
-        formH.setValue(attr, silence[attr]);
+        formHelper.setValue(attr, silence[attr]);
       });
       silence.matchers.forEach((matcher) =>
         addMatcher(matcher.name, matcher.value, matcher.isRegex)
@@ -574,10 +574,10 @@ describe('SilenceFormComponent', () => {
       expect(router.navigate).not.toHaveBeenCalled();
     });
 
-    it('should route back to "/silence" on success', () => {
+    it('should route back to previous tab on success', () => {
       fillAndSubmit();
       expect(form.valid).toBeTruthy();
-      expect(router.navigate).toHaveBeenCalledWith(['/silence']);
+      expect(router.navigate).toHaveBeenCalledWith(['/monitoring'], { fragment: 'silences' });
     });
 
     it('should create a silence', () => {
index 8e984e907584e3d1e04529ba370fd2e169cd446c..d5ef6b35e6f596e2057b4a95e4a5227b391f490f 100644 (file)
@@ -96,8 +96,8 @@ export class SilenceFormComponent {
   }
 
   private chooseMode() {
-    this.edit = this.router.url.startsWith('/silence/edit');
-    this.recreate = this.router.url.startsWith('/silence/recreate');
+    this.edit = this.router.url.startsWith('/monitoring/silence/edit');
+    this.recreate = this.router.url.startsWith('/monitoring/silence/recreate');
     if (this.edit) {
       this.action = this.actionLabels.EDIT;
     } else if (this.recreate) {
@@ -294,7 +294,7 @@ export class SilenceFormComponent {
     }
     this.prometheusService.setSilence(this.getSubmitData()).subscribe(
       (resp) => {
-        this.router.navigate(['/silence']);
+        this.router.navigate(['/monitoring'], { fragment: 'silences' });
         this.notificationService.show(
           NotificationType.success,
           this.getNotificationTile(resp.body['silenceId']),
index f67f127cd03cf29fc4fa41c899dc6c33d038a5bd..fef517d857113448daa3cc1632abbb945b331932 100644 (file)
@@ -1,5 +1,3 @@
-<cd-prometheus-tabs></cd-prometheus-tabs>
-
 <cd-table [data]="silences"
           [columns]="columns"
           [forceIdentifier]="true"
index 10eeb061dfb7018ddcf2db0c29fba351b69f4d91..9b69f579557f0845d0ed2348992adf042680ebf1 100644 (file)
@@ -20,7 +20,6 @@ import { TableActionsComponent } from '../../../../shared/datatable/table-action
 import { NotificationType } from '../../../../shared/enum/notification-type.enum';
 import { NotificationService } from '../../../../shared/services/notification.service';
 import { SharedModule } from '../../../../shared/shared.module';
-import { PrometheusTabsComponent } from '../prometheus-tabs/prometheus-tabs.component';
 import { SilenceListComponent } from './silence-list.component';
 
 describe('SilenceListComponent', () => {
@@ -38,7 +37,7 @@ describe('SilenceListComponent', () => {
       RouterTestingModule,
       HttpClientTestingModule
     ],
-    declarations: [SilenceListComponent, PrometheusTabsComponent],
+    declarations: [SilenceListComponent],
     providers: [i18nProviders]
   });
 
index 4cab7463a53914f2cb0fca735bd0d6763939a363..c423b001c51d4aa341beb8db56a74ce9208c4e1d 100644 (file)
@@ -24,7 +24,7 @@ import { AuthStorageService } from '../../../../shared/services/auth-storage.ser
 import { NotificationService } from '../../../../shared/services/notification.service';
 import { URLBuilderService } from '../../../../shared/services/url-builder.service';
 
-const BASE_URL = 'silence';
+const BASE_URL = 'monitoring/silence';
 
 @Component({
   providers: [{ provide: URLBuilderService, useValue: new URLBuilderService(BASE_URL) }],
@@ -68,6 +68,7 @@ export class SilenceListComponent implements OnInit {
         permission: 'create',
         icon: Icons.add,
         routerLink: () => this.urlBuilder.getCreate(),
+        preserveFragment: true,
         canBePrimary: (selection: CdTableSelection) => !selection.hasSingleSelection,
         name: this.actionLabels.CREATE
       },
@@ -82,6 +83,7 @@ export class SilenceListComponent implements OnInit {
           !selectionExpired(selection),
         icon: Icons.copy,
         routerLink: () => this.urlBuilder.getRecreate(this.selection.first().id),
+        preserveFragment: true,
         name: this.actionLabels.RECREATE
       },
       {
@@ -95,6 +97,7 @@ export class SilenceListComponent implements OnInit {
           (selection.first().cdExecuting && !selectionExpired(selection)) ||
           selectionExpired(selection),
         routerLink: () => this.urlBuilder.getEdit(this.selection.first().id),
+        preserveFragment: true,
         name: this.actionLabels.EDIT
       },
       {
index 3ca38d9735dc39a74adc627e4f872db849c670d7..cca56a32dff77de9537f6297bc25f8326bfa7a74 100644 (file)
@@ -4,7 +4,8 @@
       [ngClass]="{ 'active': last }"
       class="breadcrumb-item">
     <a *ngIf="!last && crumb.path !== null"
-       [routerLink]="crumb.path">{{ crumb.text }}</a>
+       [routerLink]="crumb.path"
+       preserveFragment>{{ crumb.text }}</a>
     <span *ngIf="last || crumb.path === null">{{ crumb.text }}</span>
   </li>
 </ol>
index 681fa6dc25f3af7c230eed98068fc127dba36cbe..309214d72afb29196dd550cb589f118cb8f707cd 100644 (file)
                routerLink="/logs">Logs</a>
           </li>
           <li routerLinkActive="active"
-              class="tc_submenuitem tc_submenuitem_prometheus"
+              class="tc_submenuitem tc_submenuitem_monitoring"
               *ngIf="prometheusConfigured && permissions.prometheus.read">
             <a i18n
-               routerLink="/alerts">Alerts</a>
-          </li>
-          <li routerLinkActive="active"
-              class="tc_submenuitem tc_submenuitem_prometheus"
-              *ngIf="prometheusConfigured && permissions.prometheus.read">
-            <a i18n
-               routerLink="/silence">Silences</a>
+               routerLink="/monitoring">Monitoring</a>
           </li>
         </ul>
       </li>
index 3e0cd6f1d13fd136c937ef072a18b661cd6aa34c..b5ecd68fd55f9ef5d25c6aa79c5fad704b0e29d2 100644 (file)
@@ -40,7 +40,9 @@ export class NavigationComponent implements OnInit {
       this.summaryData = data;
     });
     if (this.permissions.configOpt.read) {
-      this.prometheusService.ifAlertmanagerConfigured(() => (this.prometheusConfigured = true));
+      this.prometheusService.ifAlertmanagerConfigured(() => {
+        this.prometheusConfigured = true;
+      });
     }
   }
 
index e0d6c209f923c5514a9c4151d0aa66d67acd4f12..65ad67defb4c18c1eb12ffcae7ab79aa8a5402bc 100644 (file)
@@ -5,7 +5,8 @@
             class="btn btn-sm btn-primary"
             [ngClass]="{'disabled': disableSelectionAction(action)}"
             (click)="useClickAction(action)"
-            [routerLink]="useRouterLink(action)">
+            [routerLink]="useRouterLink(action)"
+            [preserveFragment]="action.preserveFragment ? '' : null">
       <i class="fa fa-fw {{ action.icon }}"></i><span>{{ action.name }}</span>
     </button>
   </ng-container>
@@ -28,7 +29,8 @@
           data-toggle="tooltip" title="{{ useDisableDesc(action) }}">
         <a class="dropdown-item"
            (click)="useClickAction(action)"
-           [routerLink]="useRouterLink(action)">
+           [routerLink]="useRouterLink(action)"
+           [preserveFragment]="action.preserveFragment ? '' : null">
           <i class="fa fa-fw {{ action.icon }}"></i><span>{{ action.name }}</span>
         </a>
       </li>
index c5c6fab131e1d530d0de11e4901ff85717ec1b82..ac8dcb61a98d05712921687d93dd8b85bf93e113 100644 (file)
@@ -6,6 +6,8 @@ export class CdTableAction {
   // or none if it's not needed
   routerLink?: string | Function;
 
+  preserveFragment? = false;
+
   // This is the function that will be triggered on a click event if defined
   click?: Function;