]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Extract documentation link to a component
authorTiago Melo <tmelo@suse.com>
Thu, 30 Jul 2020 20:31:13 +0000 (20:31 +0000)
committerTiago Melo <tmelo@suse.com>
Tue, 4 Aug 2020 11:56:24 +0000 (11:56 +0000)
Fixes: https://tracker.ceph.com/issues/36565
Fixes: https://tracker.ceph.com/issues/43375
Signed-off-by: Tiago Melo <tmelo@suse.com>
33 files changed:
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/inventory/inventory.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/osd/osd-form/osd-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/active-alert-list/active-alert-list.component.html
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-list-helper.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/rules-list/rules-list.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.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/services/services.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-501/nfs-501.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-501/nfs-501.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form/nfs-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-501/rgw-501.component.ts
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.html
src/pybind/mgr/dashboard/frontend/src/app/core/navigation/dashboard-help/dashboard-help.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/grafana/grafana.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/grafana/grafana.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/grafana/grafana.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/orchestrator-doc-panel/orchestrator-doc-panel.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/components/orchestrator-doc-panel/orchestrator-doc-panel.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.ts [new file with mode: 0644]

index 276629cd413b2490d87a73282dc58265d8e7dd82..0103c11249a1bf2aa5daa7c622a35d42adfd3c51 100644 (file)
@@ -4,9 +4,8 @@
                 *ngIf="available === false"
                 title="iSCSI Targets not available"
                 i18n-title>
-  <ng-container i18n>Please consult the <a href="{{docsUrl}}"
-       target="_blank">documentation</a>
-    on how to configure and enable the iSCSI Targets management functionality.</ng-container>
+  <ng-container i18n>Please consult the <cd-doc section="iscsi"></cd-doc> on
+    how to configure and enable the iSCSI Targets management functionality.</ng-container>
 
   <ng-container *ngIf="status">
     <br>
index dd96295ca0e00023e9048ec10bdf1787daa539cf..686b1ba4589e6101d89cbd7fd715fe4ff8aec55b 100644 (file)
@@ -17,11 +17,9 @@ import { CdTableSelection } from '../../../shared/models/cd-table-selection';
 import { FinishedTask } from '../../../shared/models/finished-task';
 import { Permission } from '../../../shared/models/permissions';
 import { Task } from '../../../shared/models/task';
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
 import { NotAvailablePipe } from '../../../shared/pipes/not-available.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
 import { ModalService } from '../../../shared/services/modal.service';
-import { SummaryService } from '../../../shared/services/summary.service';
 import { TaskListService } from '../../../shared/services/task-list.service';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
 import { IscsiTargetDiscoveryModalComponent } from '../iscsi-target-discovery-modal/iscsi-target-discovery-modal.component';
@@ -38,7 +36,6 @@ export class IscsiTargetListComponent extends ListWithDetails implements OnInit,
 
   available: boolean = undefined;
   columns: CdTableColumn[];
-  docsUrl: string;
   modalRef: NgbModalRef;
   permission: Permission;
   selection = new CdTableSelection();
@@ -62,9 +59,7 @@ export class IscsiTargetListComponent extends ListWithDetails implements OnInit,
     private authStorageService: AuthStorageService,
     private iscsiService: IscsiService,
     private taskListService: TaskListService,
-    private cephReleaseNamePipe: CephReleaseNamePipe,
     private notAvailablePipe: NotAvailablePipe,
-    private summaryservice: SummaryService,
     private modalService: ModalService,
     private taskWrapper: TaskWrapperService,
     public actionLabels: ActionLabelsI18n
@@ -145,11 +140,7 @@ export class IscsiTargetListComponent extends ListWithDetails implements OnInit,
           this.settings = settings;
         });
       } else {
-        this.summaryservice.subscribeOnce((summary) => {
-          const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-          this.docsUrl = `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/#enabling-iscsi-management`;
-          this.status = result.message;
-        });
+        this.status = result.message;
       }
     });
   }
index a529924d93e53b3805fbeebe0b005dba9cecccb9..da0f1a541a14071a98cecdc015fa137207d0e27e 100644 (file)
@@ -16,7 +16,6 @@ export class InventoryComponent implements OnChanges, OnInit {
   icons = Icons;
 
   hasOrchestrator = false;
-  docsUrl: string;
 
   devices: Array<InventoryDevice> = [];
 
index 7a3538e6e2038bb444c0b3341a140cf2f5582f16..651ae814ffaef55481feff3600488e6960365b43 100644 (file)
@@ -60,7 +60,6 @@ export class OsdFormComponent extends CdForm implements OnInit {
   featureList: OsdFeature[] = [];
 
   hasOrchestrator = true;
-  docsUrl: string;
 
   constructor(
     public actionLabels: ActionLabelsI18n,
index 8f9605b5bb2857f89894331ff5b215a3decf8555..278bc4ddc460cc58e8157187bd0689b83f86b1da 100644 (file)
@@ -2,11 +2,9 @@
 
 <cd-alert-panel *ngIf="!isAlertmanagerConfigured"
                 type="info"
-                i18n>To see all active Prometheus alerts, please
-  provide the URL to the API of Prometheus' Alertmanager as described
-  in the <a href="{{docsUrl}}"
-            target="_blank">documentation</a>.
-</cd-alert-panel>
+                i18n>To see all active Prometheus alerts, please provide
+  the URL to the API of Prometheus' Alertmanager as described
+  in the <cd-doc section="prometheus"></cd-doc>.</cd-alert-panel>
 
 <cd-table *ngIf="isAlertmanagerConfigured"
           [data]="prometheusAlertService.alerts"
index 7c0e437d7a2eba0ffc91b855aa562c9c301ecb75..7971de92d31632de20f1015959d71150b7fa34f6 100644 (file)
@@ -8,10 +8,8 @@ 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 { CephReleaseNamePipe } from '../../../../shared/pipes/ceph-release-name.pipe';
 import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
 import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
-import { SummaryService } from '../../../../shared/services/summary.service';
 import { URLBuilderService } from '../../../../shared/services/url-builder.service';
 import { PrometheusListHelper } from '../prometheus-list-helper';
 
@@ -43,11 +41,9 @@ export class ActiveAlertListComponent extends PrometheusListHelper implements On
     public prometheusAlertService: PrometheusAlertService,
     private urlBuilder: URLBuilderService,
     private cdDatePipe: CdDatePipe,
-    @Inject(PrometheusService) prometheusService: PrometheusService,
-    @Inject(SummaryService) summaryService: SummaryService,
-    @Inject(CephReleaseNamePipe) cephReleaseNamePipe: CephReleaseNamePipe
+    @Inject(PrometheusService) prometheusService: PrometheusService
   ) {
-    super(prometheusService, summaryService, cephReleaseNamePipe);
+    super(prometheusService);
     this.permission = this.authStorageService.getPermissions().prometheus;
     this.tableActions = [
       {
index f914ed95c7a2b1e50907049fcc3ab62c6f77cb1f..6bce8843ac2ec8ce83ffc6a386cf9c8e7be5c01f 100644 (file)
@@ -2,21 +2,14 @@ import { Directive, OnInit } from '@angular/core';
 
 import { PrometheusService } from '../../../shared/api/prometheus.service';
 import { ListWithDetails } from '../../../shared/classes/list-with-details.class';
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
-import { SummaryService } from '../../../shared/services/summary.service';
 
 @Directive()
 // tslint:disable-next-line: directive-class-suffix
 export class PrometheusListHelper extends ListWithDetails implements OnInit {
   public isPrometheusConfigured = false;
   public isAlertmanagerConfigured = false;
-  public docsUrl = '';
 
-  constructor(
-    protected prometheusService: PrometheusService,
-    protected summaryService: SummaryService,
-    protected cephReleaseNamePipe: CephReleaseNamePipe
-  ) {
+  constructor(protected prometheusService: PrometheusService) {
     super();
   }
 
@@ -27,9 +20,5 @@ export class PrometheusListHelper extends ListWithDetails implements OnInit {
     this.prometheusService.ifPrometheusConfigured(() => {
       this.isPrometheusConfigured = true;
     });
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl = `https://docs.ceph.com/docs/${releaseName}/mgr/dashboard/#enabling-prometheus-alerting`;
-    });
   }
 }
index 8600c3df2ec9131a6db1b846c344022431f378ec..0066784a86409b49fa0cc617c5717ce9b7ed6e9e 100644 (file)
@@ -4,9 +4,7 @@
                 type="info"
                 i18n>To see all configured Prometheus alerts, please
   provide the URL to the API of Prometheus as described in
-  the <a href="{{docsUrl}}"
-         target="_blank">documentation</a>.
-</cd-alert-panel>
+  the <cd-doc section="prometheus"></cd-doc>.</cd-alert-panel>
 
 <cd-table *ngIf="isPrometheusConfigured"
           [data]="prometheusAlertService.rules"
index e98aaabe74c0adcf1f0bf8fbd2ca2d9bf794723d..01830d00e77fd51323a0c30767c4b4eb122bac64 100644 (file)
@@ -3,10 +3,8 @@ import { Component, Inject, OnInit } from '@angular/core';
 import { PrometheusService } from '../../../../shared/api/prometheus.service';
 import { CdTableColumn } from '../../../../shared/models/cd-table-column';
 import { PrometheusRule } from '../../../../shared/models/prometheus-alerts';
-import { CephReleaseNamePipe } from '../../../../shared/pipes/ceph-release-name.pipe';
 import { DurationPipe } from '../../../../shared/pipes/duration.pipe';
 import { PrometheusAlertService } from '../../../../shared/services/prometheus-alert.service';
-import { SummaryService } from '../../../../shared/services/summary.service';
 import { PrometheusListHelper } from '../prometheus-list-helper';
 
 @Component({
@@ -27,11 +25,9 @@ export class RulesListComponent extends PrometheusListHelper implements OnInit {
 
   constructor(
     public prometheusAlertService: PrometheusAlertService,
-    @Inject(PrometheusService) prometheusService: PrometheusService,
-    @Inject(SummaryService) summaryService: SummaryService,
-    @Inject(CephReleaseNamePipe) cephReleaseNamePipe: CephReleaseNamePipe
+    @Inject(PrometheusService) prometheusService: PrometheusService
   ) {
-    super(prometheusService, summaryService, cephReleaseNamePipe);
+    super(prometheusService);
   }
 
   ngOnInit() {
index 364796d876190271e9c792eaf04c18217b110a1a..2997ff3738f41ac29d193a0fa2e84b528e89f046 100644 (file)
@@ -2,11 +2,9 @@
 
 <cd-alert-panel *ngIf="!isAlertmanagerConfigured"
                 type="info"
-                i18n>To enable Silences, please provide the URL to the
-  API of the Prometheus' Alertmanager as described in
-  the <a href="{{docsUrl}}"
-         target="_blank">documentation</a>.
-</cd-alert-panel>
+                i18n>To enable Silences, please provide the URL to
+  the API of the Prometheus' Alertmanager as described in the
+  <cd-doc section="prometheus"></cd-doc>.</cd-alert-panel>
 
 <cd-table *ngIf="isAlertmanagerConfigured"
           [data]="silences"
index af48f5fdba69f3f5d32d279689be5df5752ce902..e852352ac02ae8defbe6024fb2957054842db1f7 100644 (file)
@@ -19,11 +19,9 @@ 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 { CephReleaseNamePipe } from '../../../../shared/pipes/ceph-release-name.pipe';
 import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
 import { ModalService } from '../../../../shared/services/modal.service';
 import { NotificationService } from '../../../../shared/services/notification.service';
-import { SummaryService } from '../../../../shared/services/summary.service';
 import { URLBuilderService } from '../../../../shared/services/url-builder.service';
 import { PrometheusListHelper } from '../prometheus-list-helper';
 
@@ -57,11 +55,9 @@ export class SilenceListComponent extends PrometheusListHelper {
     private urlBuilder: URLBuilderService,
     private actionLabels: ActionLabelsI18n,
     private succeededLabels: SucceededActionLabelsI18n,
-    @Inject(PrometheusService) prometheusService: PrometheusService,
-    @Inject(SummaryService) summaryService: SummaryService,
-    @Inject(CephReleaseNamePipe) cephReleaseNamePipe: CephReleaseNamePipe
+    @Inject(PrometheusService) prometheusService: PrometheusService
   ) {
-    super(prometheusService, summaryService, cephReleaseNamePipe);
+    super(prometheusService);
     this.permission = this.authStorageService.getPermissions().prometheus;
     const selectionExpired = (selection: CdTableSelection) =>
       selection.first() && selection.first().status && selection.first().status.state === 'expired';
index 48f5acc293d49cff206fec014d9a9aa5c2912f7d..2f825874f94f7843266a0df9a45174faa46e173d 100644 (file)
@@ -30,7 +30,6 @@ export class ServicesComponent extends ListWithDetails implements OnChanges, OnI
 
   checkingOrchestrator = true;
   hasOrchestrator = false;
-  docsUrl: string;
 
   columns: Array<CdTableColumn> = [];
   services: Array<CephServiceSpec> = [];
index dae183db0d6ccbe383cd298618bbdb5a07e8fad6..bcbb88125429d01f42627d1d628118f7ba848112 100644 (file)
@@ -1,6 +1,5 @@
 <cd-alert-panel type="info">
   {{ message }}<br>
-  <ng-container i18n>Please consult the <a href="{{docsUrl}}"
-                                           target="_blank">documentation</a>
-  on how to configure and enable the NFS Ganesha management functionality.</ng-container>
+  <ng-container i18n>Please consult the <cd-doc section="nfs-ganesha"></cd-doc> on how
+    to configure and enable the NFS Ganesha management functionality.</ng-container>
 </cd-alert-panel>
index 4654a5ca5c6b523915346d572d5fc1bb65c82d07..7f9172294651d0834f0d66cffc4f43b2043c6569 100644 (file)
@@ -1,33 +1,18 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
-import { SummaryService } from '../../../shared/services/summary.service';
-
 @Component({
   selector: 'cd-nfs-501',
   templateUrl: './nfs-501.component.html',
   styleUrls: ['./nfs-501.component.scss']
 })
 export class Nfs501Component implements OnInit, OnDestroy {
-  docsUrl: string;
   message = $localize`The NFS Ganesha service is not configured.`;
   routeParamsSubscribe: any;
 
-  constructor(
-    private route: ActivatedRoute,
-    private summaryService: SummaryService,
-    private cephReleaseNamePipe: CephReleaseNamePipe
-  ) {}
+  constructor(private route: ActivatedRoute) {}
 
   ngOnInit() {
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl =
-        `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/` +
-        `#configuring-nfs-ganesha-in-the-dashboard`;
-    });
-
     this.routeParamsSubscribe = this.route.params.subscribe((params: { message: string }) => {
       this.message = params.message;
     });
index f7d02b03bacd8cdb68b5928f5db9a386218f0346..a8b978c4f7a8e96cb16a6ccc270853dbedaeb60c 100644 (file)
                   *ngIf="nfsForm.getValue('access_type') === 'RW' && nfsForm.getValue('name') === 'RGW'"
                   i18n>The Object Gateway NFS backend has a number of
               limitations which will seriously affect applications writing to
-              the share. Please consult the
-              <a href="{{docsUrl}}"
-                 target="_blank"> documentation</a> for details before enabling write access.</span>
+              the share. Please consult the <cd-doc section="rgw-nfs"></cd-doc>
+              for details before enabling write access.</span>
             <span class="invalid-feedback"
                   *ngIf="nfsForm.showError('access_type', formDir, 'required')"
                   i18n>This field is required.</span>
index 4a62907fbecd0980d9de8d8e0c3885eaac007f48..7e4abf6c171c0d786c613728e0c017267ff2464a 100644 (file)
@@ -18,9 +18,7 @@ import { CdFormGroup } from '../../../shared/forms/cd-form-group';
 import { CdValidators } from '../../../shared/forms/cd-validators';
 import { FinishedTask } from '../../../shared/models/finished-task';
 import { Permission } from '../../../shared/models/permissions';
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
-import { SummaryService } from '../../../shared/services/summary.service';
 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
 import { NfsFormClientComponent } from '../nfs-form-client/nfs-form-client.component';
 
@@ -61,7 +59,6 @@ export class NfsFormComponent extends CdForm implements OnInit {
 
   action: string;
   resource: string;
-  docsUrl: string;
 
   daemonsSelections: SelectOption[] = [];
   daemonsMessages = new SelectMessages({ noOptions: $localize`There are no daemons available.` });
@@ -90,8 +87,6 @@ export class NfsFormComponent extends CdForm implements OnInit {
     private router: Router,
     private rgwUserService: RgwUserService,
     private formBuilder: CdFormBuilder,
-    private summaryservice: SummaryService,
-    private cephReleaseNamePipe: CephReleaseNamePipe,
     private taskWrapper: TaskWrapperService,
     private cdRef: ChangeDetectorRef,
     public actionLabels: ActionLabelsI18n
@@ -127,11 +122,6 @@ export class NfsFormComponent extends CdForm implements OnInit {
       this.action = this.actionLabels.CREATE;
       this.getData(promises);
     }
-
-    this.summaryservice.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl = `http://docs.ceph.com/docs/${releaseName}/radosgw/nfs/`;
-    });
   }
 
   getData(promises: Observable<any>[]) {
index b21e94f8e1a64b494e9d614144e40ca1146613a7..397c1829c9ddbcaa7426103589e8247e747ff30d 100644 (file)
                     *ngIf="form.showError('pgNum', formDir, '34')"
                     i18n>Your cluster can't handle this many PGs. Please recalculate the PG amount needed.</span>
               <span class="form-text text-muted">
-                <a i18n
-                   target="_blank"
-                   href="http://ceph.com/pgcalc">Calculation help</a>
+                <cd-doc section="pgs"
+                        docText="Calculation help"
+                        i18n-docText></cd-doc>
               </span>
               <span class="form-text text-muted"
                     *ngIf="externalPgChange"
index bfec49ceadbe8711651e3848faf8a5fccf29a82c..334d1703b0ef22ea4f9d40cb3d2343b1de1d8164 100644 (file)
@@ -1,6 +1,5 @@
 <cd-alert-panel type="info">
   {{ message }}<br>
-  <ng-container i18n>Please consult the <a href="{{docsUrl}}"
-                                           target="_blank">documentation</a>
-  on how to configure and enable the Object Gateway management functionality.</ng-container>
+  <ng-container i18n>Please consult the <cd-doc section="rgw"></cd-doc> on how
+    to configure and enable the Object Gateway management functionality.</ng-container>
 </cd-alert-panel>
index 02fbc18cdc6077b55e1323d8d67bcbfd42335b08..77bea30a6be1e6f7f35f7f96664bbbfd0b9003c9 100644 (file)
@@ -1,33 +1,18 @@
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
-import { SummaryService } from '../../../shared/services/summary.service';
-
 @Component({
   selector: 'cd-rgw-501',
   templateUrl: './rgw-501.component.html',
   styleUrls: ['./rgw-501.component.scss']
 })
 export class Rgw501Component implements OnInit, OnDestroy {
-  docsUrl: string;
   message = 'The Object Gateway service is not configured.';
   routeParamsSubscribe: any;
 
-  constructor(
-    private route: ActivatedRoute,
-    private summaryService: SummaryService,
-    private cephReleaseNamePipe: CephReleaseNamePipe
-  ) {}
+  constructor(private route: ActivatedRoute) {}
 
   ngOnInit() {
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl =
-        `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/` +
-        `#enabling-the-object-gateway-management-frontend`;
-    });
-
     this.routeParamsSubscribe = this.route.params.subscribe((params: { message: string }) => {
       this.message = params.message;
     });
index 7d092ccdd7daf5de9eb93c49028b6dbd988e2f32..161e1f0c3b067e414a3c3297ce835b58dd733b0e 100644 (file)
   <div ngbDropdownMenu>
     <a ngbDropdownItem
        [ngClass]="{'disabled': !docsUrl}"
+       class="text-capitalize"
        href="{{ docsUrl }}"
        target="_blank"
-       i18n>Documentation</a>
+       i18n>documentation</a>
     <button ngbDropdownItem
             (click)="goToApiDocs()"
             i18n>API</button>
index 8ec1046662939299609152129d276bf0ea220d6d..c5036e5b8ddacd90f51e5345a35c321fefd82c3e 100644 (file)
@@ -3,10 +3,9 @@ import { Component, OnInit, ViewChild } from '@angular/core';
 import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
 
 import { Icons } from '../../../shared/enum/icons.enum';
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
+import { DocService } from '../../../shared/services/doc.service';
 import { ModalService } from '../../../shared/services/modal.service';
-import { SummaryService } from '../../../shared/services/summary.service';
 import { AboutComponent } from '../about/about.component';
 
 @Component({
@@ -22,16 +21,14 @@ export class DashboardHelpComponent implements OnInit {
   icons = Icons;
 
   constructor(
-    private summaryService: SummaryService,
-    private cephReleaseNamePipe: CephReleaseNamePipe,
     private modalService: ModalService,
-    private authStorageService: AuthStorageService
+    private authStorageService: AuthStorageService,
+    private docService: DocService
   ) {}
 
   ngOnInit() {
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl = `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/`;
+    this.docService.subscribeOnce('dashboard', (url: string) => {
+      this.docsUrl = url;
     });
   }
 
index 917310640806dc16def2737c593ef5a11956bc93..52938b6ad10916156ba72a190fbb43ffd6720c19 100644 (file)
@@ -25,6 +25,7 @@ import { ConfigOptionComponent } from './config-option/config-option.component';
 import { ConfirmationModalComponent } from './confirmation-modal/confirmation-modal.component';
 import { CriticalConfirmationModalComponent } from './critical-confirmation-modal/critical-confirmation-modal.component';
 import { DateTimePickerComponent } from './date-time-picker/date-time-picker.component';
+import { DocComponent } from './doc/doc.component';
 import { FormModalComponent } from './form-modal/form-modal.component';
 import { GrafanaComponent } from './grafana/grafana.component';
 import { HelperComponent } from './helper/helper.component';
@@ -87,7 +88,8 @@ import { UsageBarComponent } from './usage-bar/usage-bar.component';
     TelemetryNotificationComponent,
     OrchestratorDocPanelComponent,
     OrchestratorDocModalComponent,
-    DateTimePickerComponent
+    DateTimePickerComponent,
+    DocComponent
   ],
   providers: [],
   exports: [
@@ -109,7 +111,8 @@ import { UsageBarComponent } from './usage-bar/usage-bar.component';
     PwdExpirationNotificationComponent,
     TelemetryNotificationComponent,
     OrchestratorDocPanelComponent,
-    DateTimePickerComponent
+    DateTimePickerComponent,
+    DocComponent
   ]
 })
 export class ComponentsModule {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.html
new file mode 100644 (file)
index 0000000..b90fedc
--- /dev/null
@@ -0,0 +1,2 @@
+<a href="{{ docUrl }}"
+   target="_blank">{{ docText }}</a>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.scss
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.spec.ts
new file mode 100644 (file)
index 0000000..b66e63f
--- /dev/null
@@ -0,0 +1,27 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
+import { DocComponent } from './doc.component';
+
+describe('DocComponent', () => {
+  let component: DocComponent;
+  let fixture: ComponentFixture<DocComponent>;
+
+  configureTestBed({
+    declarations: [DocComponent],
+    imports: [HttpClientTestingModule],
+    providers: [CephReleaseNamePipe]
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DocComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/doc/doc.component.ts
new file mode 100644 (file)
index 0000000..7bc2d3c
--- /dev/null
@@ -0,0 +1,23 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+import { DocService } from '../../../shared/services/doc.service';
+
+@Component({
+  selector: 'cd-doc',
+  templateUrl: './doc.component.html',
+  styleUrls: ['./doc.component.scss']
+})
+export class DocComponent implements OnInit {
+  @Input() section: string;
+  @Input() docText = $localize`documentation`;
+
+  docUrl: string;
+
+  constructor(private docService: DocService) {}
+
+  ngOnInit() {
+    this.docService.subscribeOnce(this.section, (url: string) => {
+      this.docUrl = url;
+    });
+  }
+}
index d6ac94af42aa46bfde559bf338185de903c374e5..a5a29bfbee8509f47be002b3cc48f5a0cf731f20 100644 (file)
@@ -4,17 +4,13 @@
 
 <cd-alert-panel type="info"
                 *ngIf="!grafanaExist"
-                i18n>Please consult the
-  <a href="{{ docsUrl }}"
-     target="_blank">documentation</a> on how to
-  configure and enable the monitoring functionality.</cd-alert-panel>
+                i18n>Please consult the <cd-doc section="grafana"></cd-doc> on
+  how to configure and enable the monitoring functionality.</cd-alert-panel>
 
 <cd-alert-panel type="info"
                 *ngIf="!dashboardExist"
                 i18n>Grafana Dashboard doesn't exist. Please refer to
-  <a href="{{ docsUrl }}"
-     target="_blank">documentation</a> on how to
-  add dashboards to Grafana.</cd-alert-panel>
+  <cd-doc section="grafana"></cd-doc> on how to add dashboards to Grafana.</cd-alert-panel>
 
 <ng-container *ngIf="grafanaExist && dashboardExist">
   <div class="row">
index fb7747cabf5bd649362bda23a2f9b81df4f86ccc..f597f6074f4b906a4b375af64cff7ad3176e2637 100644 (file)
@@ -11,6 +11,7 @@ import { SummaryService } from '../../../shared/services/summary.service';
 import { SettingsService } from '../../api/settings.service';
 import { CephReleaseNamePipe } from '../../pipes/ceph-release-name.pipe';
 import { AlertPanelComponent } from '../alert-panel/alert-panel.component';
+import { DocComponent } from '../doc/doc.component';
 import { LoadingPanelComponent } from '../loading-panel/loading-panel.component';
 import { GrafanaComponent } from './grafana.component';
 
@@ -19,7 +20,7 @@ describe('GrafanaComponent', () => {
   let fixture: ComponentFixture<GrafanaComponent>;
 
   configureTestBed({
-    declarations: [GrafanaComponent, AlertPanelComponent, LoadingPanelComponent],
+    declarations: [GrafanaComponent, AlertPanelComponent, LoadingPanelComponent, DocComponent],
     imports: [NgbAlertModule, HttpClientTestingModule, RouterTestingModule, FormsModule],
     providers: [CephReleaseNamePipe, SettingsService, SummaryService]
   });
index 827210ccb6b3ef41f44d2ecf5e155f5af7aafe8a..18649d48870c2eeccc35f45457a9e7211f42a52e 100644 (file)
@@ -2,8 +2,6 @@ import { Component, Input, OnChanges, OnInit } from '@angular/core';
 import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
 
 import { Icons } from '../../../shared/enum/icons.enum';
-import { CephReleaseNamePipe } from '../../../shared/pipes/ceph-release-name.pipe';
-import { SummaryService } from '../../../shared/services/summary.service';
 import { SettingsService } from '../../api/settings.service';
 
 @Component({
@@ -36,14 +34,7 @@ export class GrafanaComponent implements OnInit, OnChanges {
   @Input()
   uid: string;
 
-  docsUrl: string;
-
-  constructor(
-    private summaryService: SummaryService,
-    private sanitizer: DomSanitizer,
-    private settingsService: SettingsService,
-    private cephReleaseNamePipe: CephReleaseNamePipe
-  ) {
+  constructor(private sanitizer: DomSanitizer, private settingsService: SettingsService) {
     this.grafanaTimes = [
       {
         name: $localize`Last 5 minutes`,
@@ -176,13 +167,6 @@ export class GrafanaComponent implements OnInit, OnChanges {
       three: 'grafana_three'
     };
 
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl =
-        `http://docs.ceph.com/docs/${releaseName}/mgr/dashboard/` +
-        `#enabling-the-embedding-of-grafana-dashboards`;
-    });
-
     this.settingsService.ifSettingConfigured('api/grafana/url', (url) => {
       this.grafanaExist = true;
       this.loading = false;
index 5159177ab50623b541b7e2946eba68d26cb59d5d..4c7175910b068360a9733c5edcd903b2d93c7c2d 100644 (file)
@@ -1,5 +1,4 @@
 <cd-alert-panel type="info"
-                i18n>Orchestrator is not available. Please consult the
-  <a href="{{ docsUrl }}"
-     target="_blank">documentation</a> on how to
-  configure and enable the functionality.</cd-alert-panel>
+                i18n>Orchestrator is not available.
+  Please consult the <cd-doc section="orch"></cd-doc> on how to configure and
+  enable the functionality.</cd-alert-panel>
index d4728bce702367edc7f71457e7ccf93094cf33e4..71c94ec7facdc75b569edec5e1547145bfe32a80 100644 (file)
@@ -1,25 +1,8 @@
-import { Component, OnInit } from '@angular/core';
-
-import { CephReleaseNamePipe } from '../../pipes/ceph-release-name.pipe';
-import { SummaryService } from '../../services/summary.service';
+import { Component } from '@angular/core';
 
 @Component({
   selector: 'cd-orchestrator-doc-panel',
   templateUrl: './orchestrator-doc-panel.component.html',
   styleUrls: ['./orchestrator-doc-panel.component.scss']
 })
-export class OrchestratorDocPanelComponent implements OnInit {
-  docsUrl: string;
-
-  constructor(
-    private cephReleaseNamePipe: CephReleaseNamePipe,
-    private summaryService: SummaryService
-  ) {}
-
-  ngOnInit() {
-    this.summaryService.subscribeOnce((summary) => {
-      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
-      this.docsUrl = `http://docs.ceph.com/docs/${releaseName}/mgr/orchestrator/`;
-    });
-  }
-}
+export class OrchestratorDocPanelComponent {}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.spec.ts
new file mode 100644 (file)
index 0000000..db0825e
--- /dev/null
@@ -0,0 +1,69 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { TestBed } from '@angular/core/testing';
+
+import { Subscriber } from 'rxjs';
+
+import { configureTestBed } from '../../../testing/unit-test-helper';
+import { SharedModule } from '../shared.module';
+import { DocService } from './doc.service';
+
+describe('DocService', () => {
+  let service: DocService;
+
+  configureTestBed({ imports: [HttpClientTestingModule, SharedModule] });
+
+  beforeEach(() => {
+    service = TestBed.inject(DocService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+
+  it('should return full URL', () => {
+    expect(service.urlGenerator('foo', 'iscsi')).toBe(
+      'http://docs.ceph.com/docs/foo/mgr/dashboard/#enabling-iscsi-management'
+    );
+  });
+
+  describe('Name of the group', () => {
+    let result: string;
+    let i: number;
+
+    const nextSummary = (newData: any) => service['releaseDataSource'].next(newData);
+
+    const callback = (response: string) => {
+      i++;
+      result = response;
+    };
+
+    beforeEach(() => {
+      i = 0;
+      result = undefined;
+      nextSummary(undefined);
+    });
+
+    it('should call subscribeOnce without releaseName', () => {
+      const subscriber = service.subscribeOnce('prometheus', callback);
+
+      expect(subscriber).toEqual(jasmine.any(Subscriber));
+      expect(i).toBe(0);
+      expect(result).toEqual(undefined);
+    });
+
+    it('should call subscribeOnce with releaseName', () => {
+      const subscriber = service.subscribeOnce('prometheus', callback);
+
+      expect(subscriber).toEqual(jasmine.any(Subscriber));
+      expect(i).toBe(0);
+      expect(result).toEqual(undefined);
+
+      nextSummary('foo');
+      expect(result).toEqual(
+        'http://docs.ceph.com/docs/foo/mgr/dashboard/#enabling-prometheus-alerting'
+      );
+      expect(i).toBe(1);
+      expect(subscriber.closed).toBe(true);
+    });
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/doc.service.ts
new file mode 100644 (file)
index 0000000..4d3ab0f
--- /dev/null
@@ -0,0 +1,57 @@
+import { Injectable } from '@angular/core';
+
+import { BehaviorSubject, Subscription } from 'rxjs';
+import { filter, first, map } from 'rxjs/operators';
+
+import { CephReleaseNamePipe } from '../pipes/ceph-release-name.pipe';
+import { SummaryService } from './summary.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class DocService {
+  private releaseDataSource = new BehaviorSubject<string>(null);
+  releaseData$ = this.releaseDataSource.asObservable();
+
+  constructor(
+    private summaryservice: SummaryService,
+    private cephReleaseNamePipe: CephReleaseNamePipe
+  ) {
+    this.summaryservice.subscribeOnce((summary) => {
+      const releaseName = this.cephReleaseNamePipe.transform(summary.version);
+      this.releaseDataSource.next(releaseName);
+    });
+  }
+
+  urlGenerator(release: string, section: string): string {
+    const domain = `http://docs.ceph.com/docs/${release}/`;
+
+    const sections = {
+      iscsi: `${domain}mgr/dashboard/#enabling-iscsi-management`,
+      prometheus: `${domain}mgr/dashboard/#enabling-prometheus-alerting`,
+      'nfs-ganesha': `${domain}mgr/dashboard/#configuring-nfs-ganesha-in-the-dashboard`,
+      'rgw-nfs': `${domain}radosgw/nfs`,
+      rgw: `${domain}mgr/dashboard/#enabling-the-object-gateway-management-frontend`,
+      dashboard: `${domain}mgr/dashboard`,
+      grafana: `${domain}mgr/dashboard/#enabling-the-embedding-of-grafana-dashboards`,
+      orch: `${domain}mgr/orchestrator`,
+      pgs: `http://ceph.com/pgcalc`
+    };
+
+    return sections[section];
+  }
+
+  subscribeOnce(
+    section: string,
+    next: (release: string) => void,
+    error?: (error: any) => void
+  ): Subscription {
+    return this.releaseData$
+      .pipe(
+        filter((value) => !!value),
+        map((release) => this.urlGenerator(release, section)),
+        first()
+      )
+      .subscribe(next, error);
+  }
+}