Created Download Button Component.
Added a Download and Copy-to-Clipboard button for the Cluster and Audit Logs in the Log section. Also added a flag in the current Copy2ClipboardButton directive to indicate the incoming string is actually a log.
Fixes: https://tracker.ceph.com/issues/47498
Signed-off-by: Nizamudeen A <nia@redhat.com>
import { TreeModule } from '@circlon/angular-tree-component';
import {
NgbDatepickerModule,
+ NgbDropdownModule,
NgbNavModule,
NgbPopoverModule,
NgbTimepickerModule,
NgBootstrapFormValidationModule,
CephSharedModule,
NgbDatepickerModule,
- NgbPopoverModule
+ NgbPopoverModule,
+ NgbDropdownModule
],
declarations: [
HostsComponent,
<ng-template ngbNavContent>
<div class="card bg-light mb-3"
*ngIf="clog">
+ <div class="btn-group"
+ role="group"
+ (click)="logToText(clog)"
+ *ngIf="clog.length">
+ <cd-download-button [objectItem]="clog"
+ [textItem]="logText"
+ fileName="cluster_log">
+ </cd-download-button>
+ <button type="button"
+ class="btn btn-light"
+ [cdCopy2ClipboardButton]="logText"
+ [byId]="false"></button>
+ </div>
<div class="card-body">
<p *ngFor="let line of clog">
<span class="timestamp">{{ line.stamp | cdDate }}</span>
<ng-template ngbNavContent>
<div class="card bg-light mb-3"
*ngIf="audit_log">
+ <div class="btn-group"
+ role="group"
+ (click)="logToText(audit_log)"
+ *ngIf="audit_log.length">
+ <cd-download-button [objectItem]="audit_log"
+ [textItem]="logText"
+ fileName="audit_log">
+ </cd-download-button>
+ <button type="button"
+ class="btn btn-light"
+ [cdCopy2ClipboardButton]="logText"
+ [byId]="false"></button>
+ </div>
<div class="card-body">
<p *ngFor="let line of audit_log">
<span class="timestamp">{{ line.stamp | cdDate }}</span>
}
.card {
+ .btn-group {
+ margin-top: -45px;
+ position: absolute;
+ right: 0;
+ }
+
div p {
display: flex;
clog: Array<any>;
audit_log: Array<any>;
icons = Icons;
+ logText: string;
interval: number;
priorities: Array<{ name: string; value: string }> = [
return false;
}
+
+ logToText(log: object) {
+ this.logText = '';
+ for (const line of Object.keys(log)) {
+ this.logText =
+ this.logText +
+ this.datePipe.transform(log[line].stamp, 'medium') +
+ '\t' +
+ log[line].priority +
+ '\t' +
+ log[line].message +
+ '\n';
+ }
+ }
}
</div>
<div class="form-group row">
<div class="cd-col-form-offset">
- <button type="button"
- class="btn btn-light mr-2"
- title="Download"
- (click)="download(report, 'telemetry_report.json')"
- i18n-title>
- <i class="fa fa-download"></i>
- </button>
- <button type="button"
- class="btn btn-light"
- cdCopy2ClipboardButton="report">
- </button>
+ <div class="btn-group"
+ role="group">
+ <cd-download-button [objectItem]="report"
+ fileName="telemetry_report">
+ </cd-download-button>
+ <button type="button"
+ class="btn btn-light"
+ cdCopy2ClipboardButton="report">
+ </button>
+ </div>
</div>
</div>
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
+import { DownloadButtonComponent } from 'app/shared/components/download-button/download-button.component';
import _ from 'lodash';
import { ToastrModule } from 'ngx-toastr';
import { of as observableOf } from 'rxjs';
import { configureTestBed } from '../../../../testing/unit-test-helper';
import { MgrModuleService } from '../../../shared/api/mgr-module.service';
-import { TelemetryService } from '../../../shared/api/telemetry.service';
import { LoadingPanelComponent } from '../../../shared/components/loading-panel/loading-panel.component';
-import { TextToDownloadService } from '../../../shared/services/text-to-download.service';
import { SharedModule } from '../../../shared/shared.module';
import { TelemetryComponent } from './telemetry.component';
let component: TelemetryComponent;
let fixture: ComponentFixture<TelemetryComponent>;
let mgrModuleService: MgrModuleService;
- let telemetryService: TelemetryService;
let options: any;
let configs: any;
let httpTesting: HttpTestingController;
ToastrModule.forRoot()
]
},
- [LoadingPanelComponent]
+ [LoadingPanelComponent, DownloadButtonComponent]
);
describe('configForm', () => {
});
describe('previewForm', () => {
- const reportText = {
- testA: 'testA',
- testB: 'testB'
- };
-
beforeEach(() => {
fixture = TestBed.createComponent(TelemetryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- telemetryService = TestBed.inject(TelemetryService);
httpTesting = TestBed.inject(HttpTestingController);
router = TestBed.inject(Router);
spyOn(router, 'navigate');
expect(component).toBeTruthy();
});
- it('should call TextToDownloadService download function', () => {
- spyOn(telemetryService, 'getReport').and.returnValue(observableOf(reportText));
- component.ngOnInit();
-
- const downloadSpy = spyOn(TestBed.inject(TextToDownloadService), 'download');
- const filename = 'reportText.json';
- component.download(reportText, filename);
- expect(downloadSpy).toHaveBeenCalledWith(JSON.stringify(reportText, null, 2), filename);
- });
-
it('should submit', () => {
component.onSubmit();
const req = httpTesting.expectOne('api/telemetry');
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({
selector: 'cd-telemetry',
private notificationService: NotificationService,
private router: Router,
private telemetryService: TelemetryService,
- private textToDownloadService: TextToDownloadService,
private telemetryNotificationService: TelemetryNotificationService
) {
super();
);
}
- download(report: object, fileName: string) {
- this.textToDownloadService.download(JSON.stringify(report, null, 2), fileName);
- }
-
disableModule(message: string = null, followUpFunc: Function = null) {
this.telemetryService.enable(false).subscribe(() => {
this.telemetryNotificationService.setVisibility(true);
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 { DownloadButtonComponent } from './download-button/download-button.component';
import { FormModalComponent } from './form-modal/form-modal.component';
import { GrafanaComponent } from './grafana/grafana.component';
import { HelperComponent } from './helper/helper.component';
TelemetryNotificationComponent,
OrchestratorDocPanelComponent,
DateTimePickerComponent,
- DocComponent
+ DocComponent,
+ DownloadButtonComponent
],
providers: [],
exports: [
TelemetryNotificationComponent,
OrchestratorDocPanelComponent,
DateTimePickerComponent,
- DocComponent
+ DocComponent,
+ DownloadButtonComponent
]
})
export class ComponentsModule {}
--- /dev/null
+<div ngbDropdown
+ placement="bottom-right">
+ <button type="button"
+ [title]="title"
+ class="btn btn-light dropdown-toggle-split"
+ ngbDropdownToggle>
+ <i [ngClass]="[icons.download]"></i>
+ </button>
+ <div ngbDropdownMenu>
+ <button ngbDropdownItem
+ (click)="download('json')"
+ *ngIf="objectItem">
+ <i [ngClass]="[icons.json]"></i>
+ <span>JSON</span>
+ </button>
+ <button ngbDropdownItem
+ (click)="download()"
+ *ngIf="textItem">
+ <i [ngClass]="[icons.text]"></i>
+ <span>Text</span>
+ </button>
+ </div>
+</div>
--- /dev/null
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { TextToDownloadService } from '../../services/text-to-download.service';
+import { DownloadButtonComponent } from './download-button.component';
+
+describe('DownloadButtonComponent', () => {
+ let component: DownloadButtonComponent;
+ let fixture: ComponentFixture<DownloadButtonComponent>;
+
+ configureTestBed({
+ declarations: [DownloadButtonComponent],
+ providers: [TextToDownloadService]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DownloadButtonComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('should call download function', () => {
+ component.objectItem = {
+ testA: 'testA',
+ testB: 'testB'
+ };
+ const downloadSpy = spyOn(TestBed.inject(TextToDownloadService), 'download');
+ component.fileName = `${'reportText.json'}_${new Date().toLocaleDateString()}`;
+ component.download('json');
+ expect(downloadSpy).toHaveBeenCalledWith(
+ JSON.stringify(component.objectItem, null, 2),
+ `${component.fileName}.json`
+ );
+ });
+});
--- /dev/null
+import { Component, Input } from '@angular/core';
+
+import { Icons } from '../../enum/icons.enum';
+import { TextToDownloadService } from '../../services/text-to-download.service';
+
+@Component({
+ selector: 'cd-download-button',
+ templateUrl: './download-button.component.html',
+ styleUrls: ['./download-button.component.scss']
+})
+export class DownloadButtonComponent {
+ @Input() objectItem: object;
+ @Input() textItem: string;
+ @Input() fileName: any;
+ @Input() title = $localize`Download`;
+
+ icons = Icons;
+ constructor(private textToDownloadService: TextToDownloadService) {}
+
+ download(format?: string) {
+ this.fileName = `${this.fileName}_${new Date().toLocaleDateString()}`;
+ if (format === 'json') {
+ this.textToDownloadService.download(
+ JSON.stringify(this.objectItem, null, 2),
+ `${this.fileName}.json`
+ );
+ } else {
+ this.textToDownloadService.download(this.textItem, `${this.fileName}.txt`);
+ }
+ }
+}
export class Copy2ClipboardButtonDirective implements OnInit {
@Input()
private cdCopy2ClipboardButton: string;
+ @Input()
+ byId = true;
constructor(
private elementRef: ElementRef,
onClick() {
try {
const browser = detect();
- const text = this.getText();
+ const text = this.byId ? this.getText() : this.cdCopy2ClipboardButton;
const toastrFn = () => {
this.toastr.success('Copied text to the clipboard successfully.');
};
download = 'fa fa-download', // Download
upload = 'fa fa-upload', // Upload
close = 'fa fa-times', // Close
+ json = 'fa fa-file-code-o', // JSON file
+ text = 'fa fa-file-text', // Text file
/* Icons for special effect */
large = 'fa fa-lg', // icon becomes 33% larger