From 6e1193e30b32b05a774eeb01f473c091c52e00ad Mon Sep 17 00:00:00 2001 From: Dylan van Dijk <99623131+Banshaan@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:16:34 +0200 Subject: [PATCH] Moved error handling of subscriptions inside the subscription (#509) * Moved error handling of subscriptions inside the subscription * Moved handleError function to its own class and fixed imports * Fixed a little issue --------- Co-authored-by: dylan van dijk --- .../debug/debug-tree/debug-tree.component.ts | 20 ++- src/app/debug/debug.component.ts | 31 ++-- .../filter-side-drawer.component.ts | 39 +++-- .../table-settings-modal.component.ts | 67 ++++--- src/app/debug/table/table.component.ts | 137 +++++++++------ .../edit-display/edit-display.component.ts | 32 ++-- .../shared/classes/error-handling.service.ts | 24 +++ src/app/shared/services/http.service.ts | 165 ++++++------------ .../test/clone-modal/clone-modal.component.ts | 21 ++- src/app/test/test.component.ts | 91 ++++++---- 10 files changed, 355 insertions(+), 272 deletions(-) create mode 100644 src/app/shared/classes/error-handling.service.ts diff --git a/src/app/debug/debug-tree/debug-tree.component.ts b/src/app/debug/debug-tree/debug-tree.component.ts index 364e0e49..83ddfd6e 100644 --- a/src/app/debug/debug-tree/debug-tree.component.ts +++ b/src/app/debug/debug-tree/debug-tree.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnDestroy, Output, ViewChild } from '@angular/core'; import { Report } from '../../shared/interfaces/report'; import { HelperService } from '../../shared/services/helper.service'; -import { Observable, Subscription } from 'rxjs'; +import { Observable, Subscription, catchError, of } from 'rxjs'; import { HttpService } from '../../shared/services/http.service'; import { SettingsService } from '../../shared/services/settings.service'; import { @@ -22,6 +22,7 @@ import { } from '@ng-bootstrap/ng-bootstrap'; import { ButtonComponent } from '../../shared/components/button/button.component'; import { ReportHierarchyTransformer } from '../../shared/classes/report-hierarchy-transformer'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ selector: 'app-debug-tree', @@ -59,6 +60,7 @@ export class DebugTreeComponent implements OnDestroy { private helperService: HelperService, private httpService: HttpService, private settingsService: SettingsService, + private errorHandler: ErrorHandling, ) { this.subscribeToSettingsServiceObservables(); } @@ -88,11 +90,10 @@ export class DebugTreeComponent implements OnDestroy { checkUnmatchedCheckpoints(reports: Report[], currentView: any) { for (let report of reports) { if (report.storageName === currentView.storageName) { - this.httpService - .getUnmatchedCheckpoints(report.storageName, report.storageId, currentView.name) - .subscribe((unmatched: any) => { - this.hideCheckpoints(unmatched, this.tree.elements.toArray()); - }); + this.httpService.getUnmatchedCheckpoints(report.storageName, report.storageId, currentView.name).subscribe({ + next: (unmatched: any) => this.hideCheckpoints(unmatched, this.tree.elements.toArray()), + error: () => catchError(this.errorHandler.handleError()), + }); } } } @@ -108,14 +109,15 @@ export class DebugTreeComponent implements OnDestroy { } subscribeToSettingsServiceObservables(): void { - this.showMultipleAtATimeSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe( - (value: boolean) => { + this.showMultipleAtATimeSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe({ + next: (value: boolean) => { this.showMultipleAtATime = value; if (!this.showMultipleAtATime) { this.removeAllReportsButOne(); } }, - ); + error: () => catchError(this.errorHandler.handleError()), + }); } hideCheckpoints(unmatched: string[], items: TreeItemComponent[]): void { diff --git a/src/app/debug/debug.component.ts b/src/app/debug/debug.component.ts index 176b8d9c..b6e09780 100644 --- a/src/app/debug/debug.component.ts +++ b/src/app/debug/debug.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; import { Report } from '../shared/interfaces/report'; -import { Subscription } from 'rxjs'; +import { Subscription, catchError } from 'rxjs'; import { DebugReportService } from './debug-report.service'; import { AngularSplitModule } from 'angular-split'; import { TableComponent } from './table/table.component'; @@ -8,6 +8,7 @@ import { ReportComponent } from '../report/report.component'; import { ToastService } from '../shared/services/toast.service'; import { HttpService } from '../shared/services/http.service'; import { View } from '../shared/interfaces/view'; +import { ErrorHandling } from '../shared/classes/error-handling.service'; @Component({ selector: 'app-debug', @@ -29,6 +30,7 @@ export class DebugComponent implements OnInit, OnDestroy { private debugReportService: DebugReportService, private httpService: HttpService, private toastService: ToastService, + private errorHandler: ErrorHandling, ) {} ngOnInit(): void { @@ -42,16 +44,22 @@ export class DebugComponent implements OnInit, OnDestroy { } retrieveViews(): void { - this.httpService.getViews().subscribe((views: View[]): void => { - this.views = views; - if (!this.currentView) { - this.currentView = this.views.find((v: View) => v.defaultView); - } + this.httpService.getViews().subscribe({ + next: (views: View[]) => { + this.views = views; + if (!this.currentView) { + this.currentView = this.views.find((v: View) => v.defaultView); + } + }, + error: () => catchError(this.errorHandler.handleError()), }); } subscribeToServices(): void { - this.viewSubscription = this.debugReportService.changeViewObservable.subscribe((view) => (this.currentView = view)); + this.viewSubscription = this.debugReportService.changeViewObservable.subscribe({ + next: (view) => (this.currentView = view), + error: () => catchError(this.errorHandler.handleError()), + }); } unsubscribeAll(): void { @@ -71,13 +79,14 @@ export class DebugComponent implements OnInit, OnDestroy { retrieveErrorsAndWarnings(): void { if (this.currentView) { - this.httpService - .getWarningsAndErrors(this.currentView.storageName) - .subscribe((value: string | undefined): void => { + this.httpService.getWarningsAndErrors(this.currentView.storageName).subscribe({ + next: (value: string | undefined): void => { if (value) { this.showErrorsAndWarnings(value); } - }); + }, + error: () => catchError(this.errorHandler.handleError()), + }); } } diff --git a/src/app/debug/filter-side-drawer/filter-side-drawer.component.ts b/src/app/debug/filter-side-drawer/filter-side-drawer.component.ts index b5baa34b..fee9ab73 100644 --- a/src/app/debug/filter-side-drawer/filter-side-drawer.component.ts +++ b/src/app/debug/filter-side-drawer/filter-side-drawer.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FilterService } from './filter.service'; -import { Subscription } from 'rxjs'; +import { Subscription, catchError } from 'rxjs'; import { animate, style, transition, trigger } from '@angular/animations'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; import { FormsModule } from '@angular/forms'; @@ -8,6 +8,7 @@ import { TitleCasePipe } from '@angular/common'; import { View } from '../../shared/interfaces/view'; import { Report } from '../../shared/interfaces/report'; import { HttpService } from '../../shared/services/http.service'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ standalone: true, @@ -42,6 +43,7 @@ export class FilterSideDrawerComponent implements OnDestroy, OnInit { constructor( protected filterService: FilterService, private httpService: HttpService, + private errorHandler: ErrorHandling, ) {} ngOnInit(): void { @@ -54,22 +56,22 @@ export class FilterSideDrawerComponent implements OnDestroy, OnInit { } setSubscriptions(): void { - this.shouldShowFilterSubscription = this.filterService.showFilter$.subscribe((show: boolean): void => { - this.shouldShowFilter = show; + this.shouldShowFilterSubscription = this.filterService.showFilter$.subscribe({ + next: (show: boolean) => (this.shouldShowFilter = show), + error: () => catchError(this.errorHandler.handleError()), }); - this.metadataLabelsSubscription = this.filterService.metadataLabels$.subscribe((metadataLabels: string[]): void => { - this.metadataLabels = metadataLabels; + this.metadataLabelsSubscription = this.filterService.metadataLabels$.subscribe({ + next: (metadataLabels: string[]) => (this.metadataLabels = metadataLabels), + error: () => catchError(this.errorHandler.handleError()), + }); + this.currentRecordsSubscription = this.filterService.currentRecords$.subscribe({ + next: (records: Map>) => (this.currentRecords = records), + error: () => catchError(this.errorHandler.handleError()), + }); + this.metadataTypesSubscription = this.filterService.metadataTypes$.subscribe({ + next: (metadataTypes: Map) => (this.metadataTypes = metadataTypes), + error: () => catchError(this.errorHandler.handleError()), }); - this.currentRecordsSubscription = this.filterService.currentRecords$.subscribe( - (records: Map>): void => { - this.currentRecords = records; - }, - ); - this.metadataTypesSubscription = this.filterService.metadataTypes$.subscribe( - (metadataTypes: Map): void => { - this.metadataTypes = metadataTypes; - }, - ); } unsubscribeAll(): void { @@ -80,9 +82,10 @@ export class FilterSideDrawerComponent implements OnDestroy, OnInit { } getFilterToolTips(): void { - this.httpService - .getUserHelp(this.currentView.storageName, this.currentView.metadataNames) - .subscribe((response: Report) => (this.toolTipSuggestions = response)); + this.httpService.getUserHelp(this.currentView.storageName, this.currentView.metadataNames).subscribe({ + next: (response: Report) => (this.toolTipSuggestions = response), + error: () => catchError(this.errorHandler.handleError()), + }); } closeFilter(): void { diff --git a/src/app/debug/table/table-settings-modal/table-settings-modal.component.ts b/src/app/debug/table/table-settings-modal/table-settings-modal.component.ts index 331c8ac1..9e263fe5 100644 --- a/src/app/debug/table/table-settings-modal/table-settings-modal.component.ts +++ b/src/app/debug/table/table-settings-modal/table-settings-modal.component.ts @@ -3,10 +3,11 @@ import { UntypedFormControl, UntypedFormGroup, ReactiveFormsModule } from '@angu import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { HttpService } from '../../../shared/services/http.service'; import { SettingsService } from '../../../shared/services/settings.service'; -import { Subscription } from 'rxjs'; +import { Subscription, catchError } from 'rxjs'; import { ToastService } from '../../../shared/services/toast.service'; import { UploadParams } from 'src/app/shared/interfaces/upload-params'; import { ToastComponent } from '../../../shared/components/toast/toast.component'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ selector: 'app-table-settings-modal', @@ -44,6 +45,7 @@ export class TableSettingsModalComponent implements OnDestroy { private httpService: HttpService, private settingsService: SettingsService, private toastService: ToastService, + private errorHandler: ErrorHandling, ) { this.subscribeToSettingsServiceObservables(); } @@ -53,26 +55,33 @@ export class TableSettingsModalComponent implements OnDestroy { } subscribeToSettingsServiceObservables(): void { - this.showMultipleAtATimeSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe( - (value: boolean): void => { + this.showMultipleAtATimeSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe({ + next: (value: boolean): void => { this.showMultipleAtATime = value; this.settingsForm.get('showMultipleFilesAtATime')?.setValue(this.showMultipleAtATime); }, - ); - this.tableSpacingSubscription = this.settingsService.tableSpacingObservable.subscribe((value: number): void => { - this.tableSpacing = value; - this.settingsForm.get('tableSpacing')?.setValue(this.tableSpacing); + error: () => catchError(this.errorHandler.handleError()), }); - this.showSearchWindowOnLoadSubscription = this.settingsService.showSearchWindowOnLoadObservable.subscribe( - (value: boolean): void => { + this.tableSpacingSubscription = this.settingsService.tableSpacingObservable.subscribe({ + next: (value: number): void => { + this.tableSpacing = value; + this.settingsForm.get('tableSpacing')?.setValue(this.tableSpacing); + }, + error: () => catchError(this.errorHandler.handleError()), + }); + this.showSearchWindowOnLoadSubscription = this.settingsService.showSearchWindowOnLoadObservable.subscribe({ + next: (value: boolean): void => { this.showSearchWindowOnLoad = value; this.settingsForm.get('showSearchWindowOnLoad')?.setValue(this.showSearchWindowOnLoad); }, - ); - - this.prettifyOnLoadSubscription = this.settingsService.prettifyOnLoadObservable.subscribe((value: boolean) => { - this.prettifyOnLoad = value; - this.settingsForm.get('prettifyOnLoad')?.setValue(this.prettifyOnLoad); + error: () => catchError(this.errorHandler.handleError()), + }); + this.prettifyOnLoadSubscription = this.settingsService.prettifyOnLoadObservable.subscribe({ + next: (value: boolean) => { + this.prettifyOnLoad = value; + this.settingsForm.get('prettifyOnLoad')?.setValue(this.prettifyOnLoad); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -122,13 +131,17 @@ export class TableSettingsModalComponent implements OnDestroy { const form: any = this.settingsForm.value; localStorage.setItem('generatorEnabled', form.generatorEnabled); localStorage.setItem('transformationEnabled', form.transformationEnabled.toString()); - this.httpService.postTransformation(form.transformation).subscribe(); + this.httpService.postTransformation(form.transformation).subscribe({ + error: catchError(this.errorHandler.handleError()), + }); const generatorEnabled: string = String(form.generatorEnabled === 'Enabled'); const data: UploadParams = { generatorEnabled: generatorEnabled, regexFilter: form.regexFilter, }; - this.httpService.postSettings(data).subscribe(); + this.httpService.postSettings(data).subscribe({ + error: catchError(this.errorHandler.handleError()), + }); this.toastService.showWarning('Reopen report to see updated XML'); this.saving = true; @@ -141,20 +154,28 @@ export class TableSettingsModalComponent implements OnDestroy { factoryReset(): void { this.settingsForm.reset(); this.settingsService.setShowMultipleAtATime(); - this.httpService.resetSettings().subscribe((response) => this.saveResponseSetting(response)); - this.httpService.getTransformation(true).subscribe((resp) => { - this.settingsForm.get('transformation')?.setValue(resp.transformation); + this.httpService.resetSettings().subscribe({ + next: (response) => this.saveResponseSetting(response), + error: () => catchError(this.errorHandler.handleError()), + }); + this.httpService.getTransformation(true).subscribe({ + next: (res) => this.settingsForm.get('transformation')?.setValue(res.transformation), + error: () => catchError(this.errorHandler.handleError()), }); } loadSettings(): void { - this.httpService.getSettings().subscribe((response) => this.saveResponseSetting(response)); + this.httpService.getSettings().subscribe({ + next: (response) => this.saveResponseSetting(response), + error: () => catchError(this.errorHandler.handleError()), + }); if (localStorage.getItem('transformationEnabled')) { this.settingsForm.get('transformationEnabled')?.setValue(localStorage.getItem('transformationEnabled') == 'true'); } - this.httpService - .getTransformation(false) - .subscribe((response) => this.settingsForm.get('transformation')?.setValue(response.transformation)); + this.httpService.getTransformation(false).subscribe({ + next: (response) => this.settingsForm.get('transformation')?.setValue(response.transformation), + error: () => catchError(this.errorHandler.handleError()), + }); } saveResponseSetting(response: any): void { diff --git a/src/app/debug/table/table.component.ts b/src/app/debug/table/table.component.ts index 47d78e2d..f82e4744 100644 --- a/src/app/debug/table/table.component.ts +++ b/src/app/debug/table/table.component.ts @@ -30,6 +30,7 @@ import { KeyValuePipe, NgClass } from '@angular/common'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { View } from '../../shared/interfaces/view'; import { OptionsSettings } from '../../shared/interfaces/options-settings'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ selector: 'app-table', @@ -135,6 +136,7 @@ export class TableComponent implements OnInit, OnDestroy { private debugReportService: DebugReportService, private tabService: TabService, private filterService: FilterService, + private errorHandler: ErrorHandling, ) {} ngOnInit(): void { @@ -150,24 +152,29 @@ export class TableComponent implements OnInit, OnDestroy { } subscribeToObservables(): void { - this.tableSpacingSubscription = this.settingsService.tableSpacingObservable.subscribe( - (value: number) => (this.tableSpacing = value), - ); - this.showMultipleFilesSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe( - (value: boolean) => (this.showMultipleFiles = value), - ); - this.showFilterSubscription = this.filterService.showFilter$.subscribe( - (show: boolean) => (this.tableSettings.showFilter = show), - ); - this.filterErrorSubscription = this.filterService.filterError$.subscribe( - (filterError: [boolean, Map]): void => { + this.tableSpacingSubscription = this.settingsService.tableSpacingObservable.subscribe({ + next: (value: number) => (this.tableSpacing = value), + error: () => catchError(this.errorHandler.handleError()), + }); + this.showMultipleFilesSubscription = this.settingsService.showMultipleAtATimeObservable.subscribe({ + next: (value: boolean) => (this.showMultipleFiles = value), + error: () => catchError(this.errorHandler.handleError()), + }); + this.showFilterSubscription = this.filterService.showFilter$.subscribe({ + next: (show: boolean) => (this.tableSettings.showFilter = show), + error: () => catchError(this.errorHandler.handleError()), + }); + this.filterErrorSubscription = this.filterService.filterError$.subscribe({ + next: (filterError: [boolean, Map]): void => { this.showFilterError = filterError[0]; this.filterErrorDetails = filterError[1]; }, - ); - this.filterContextSubscription = this.filterService.filterContext$.subscribe((context: Map) => - this.changeFilter(context), - ); + error: () => catchError(this.errorHandler.handleError()), + }); + this.filterContextSubscription = this.filterService.filterContext$.subscribe({ + next: (context: Map) => this.changeFilter(context), + error: () => catchError(this.errorHandler.handleError()), + }); } unsubscribeFromObservables(): void { @@ -195,9 +202,7 @@ export class TableComponent implements OnInit, OnDestroy { this.tableSettings.tableLoaded = true; this.toastService.showSuccess('Data loaded!'); }, - error: () => { - catchError(this.httpService.handleError()); - }, + error: () => catchError(this.errorHandler.handleError()), }); this.loadMetadataCount(); } @@ -218,9 +223,10 @@ export class TableComponent implements OnInit, OnDestroy { } loadMetadataCount(): void { - this.httpService - .getMetadataCount(this.currentView.storageName) - .subscribe((count: number) => (this.metadataCount = count)); + this.httpService.getMetadataCount(this.currentView.storageName).subscribe({ + next: (count: number) => (this.metadataCount = count), + error: () => catchError(this.errorHandler.handleError()), + }); } loadReportInProgressSettings(): void { @@ -230,18 +236,22 @@ export class TableComponent implements OnInit, OnDestroy { this.tableSettings.estimatedMemoryUsage = settings.estMemory; this.loadReportInProgressDates(); }, + error: () => catchError(this.errorHandler.handleError()), }); } loadReportInProgressDates(): void { let hasChanged: boolean = false; for (let index = 1; index <= this.tableSettings.numberOfReportsInProgress; index++) { - this.httpService.getReportInProgress(index).subscribe((report: Report) => { - this.reportsInProgress[report.correlationId] ??= report.startTime; - if (this.reportsInProgressMetThreshold(report)) { - this.hasTimedOut = true; - hasChanged = true; - } + this.httpService.getReportInProgress(index).subscribe({ + next: (report: Report) => { + this.reportsInProgress[report.correlationId] ??= report.startTime; + if (this.reportsInProgressMetThreshold(report)) { + this.hasTimedOut = true; + hasChanged = true; + } + }, + error: () => catchError(this.errorHandler.handleError()), }); } if (!hasChanged) { @@ -319,12 +329,15 @@ export class TableComponent implements OnInit, OnDestroy { this.toastService.showDanger('Could not find report that was selected.'); return; } - this.httpService.getReport(reportTab.storageId, this.currentView.storageName).subscribe((report: Report): void => { - const reportData: ReportData = { - report: report, - currentView: this.currentView!, - }; - this.tabService.openNewTab(reportData); + this.httpService.getReport(reportTab.storageId, this.currentView.storageName).subscribe({ + next: (report: Report): void => { + const reportData: ReportData = { + report: report, + currentView: this.currentView!, + }; + this.tabService.openNewTab(reportData); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -344,8 +357,9 @@ export class TableComponent implements OnInit, OnDestroy { deleteSelected(): void { const reportIds = this.helperService.getSelectedIds(this.tableSettings.reportMetadata); - this.httpService.deleteReport(reportIds, this.currentView.storageName).subscribe(() => { - this.retrieveRecords(); + this.httpService.deleteReport(reportIds, this.currentView.storageName).subscribe({ + next: () => this.retrieveRecords(), + error: () => catchError(this.errorHandler.handleError()), }); } @@ -381,6 +395,7 @@ export class TableComponent implements OnInit, OnDestroy { nodeLinkStrategy: this.currentView.nodeLinkStrategy, }; }, + error: () => catchError(this.errorHandler.handleError()), complete: () => { this.tabService.openNewCompareTab(compareReports); @@ -426,9 +441,12 @@ export class TableComponent implements OnInit, OnDestroy { } openReport(storageId: number): void { - this.httpService.getReport(storageId, this.currentView.storageName).subscribe((data: Report): void => { - data.storageName = this.currentView.storageName; - this.openReportEvent.next(data); + this.httpService.getReport(storageId, this.currentView.storageName).subscribe({ + next: (data: Report): void => { + data.storageName = this.currentView.storageName; + this.openReportEvent.next(data); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -438,21 +456,28 @@ export class TableComponent implements OnInit, OnDestroy { } openLatestReports(amount: number): void { - this.httpService.getLatestReports(amount, this.currentView.storageName).subscribe((data) => { - data.forEach((report: any) => { - this.openReportEvent.next(report); - }); + this.httpService.getLatestReports(amount, this.currentView.storageName).subscribe({ + next: (data) => { + data.forEach((report: any) => { + this.openReportEvent.next(report); + }); + }, + error: () => catchError(this.errorHandler.handleError()), }); } openReportInProgress(index: number): void { - this.httpService.getReportInProgress(index).subscribe((report) => { - this.openReportEvent.next(report); + this.httpService.getReportInProgress(index).subscribe({ + next: (report) => { + this.openReportEvent.next(report); + }, + error: () => catchError(this.errorHandler.handleError()), }); } deleteReportInProgress(index: number): void { this.httpService.deleteReportInProgress(index).subscribe({ + error: () => catchError(this.errorHandler.handleError()), complete: () => { this.loadReportInProgressSettings(); }, @@ -483,14 +508,17 @@ export class TableComponent implements OnInit, OnDestroy { } showUploadedReports(formData: any): void { - this.httpService.uploadReport(formData).subscribe((data) => { - for (let report of data) { - const reportData: ReportData = { - report: report, - currentView: this.currentView, - }; - this.tabService.openNewTab(reportData); - } + this.httpService.uploadReport(formData).subscribe({ + next: (data) => { + for (let report of data) { + const reportData: ReportData = { + report: report, + currentView: this.currentView, + }; + this.tabService.openNewTab(reportData); + } + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -571,8 +599,9 @@ export class TableComponent implements OnInit, OnDestroy { } loadReportInProgressThreshold(): void { - this.httpService.getReportsInProgressThresholdTime().subscribe((time: number) => { - this.reportsInProgressThreshold = time; + this.httpService.getReportsInProgressThresholdTime().subscribe({ + next: (time: number) => (this.reportsInProgressThreshold = time), + error: () => catchError(this.errorHandler.handleError()), }); } diff --git a/src/app/report/edit-display/edit-display.component.ts b/src/app/report/edit-display/edit-display.component.ts index 33ca7c64..cd045fc4 100644 --- a/src/app/report/edit-display/edit-display.component.ts +++ b/src/app/report/edit-display/edit-display.component.ts @@ -19,7 +19,7 @@ import { ReactiveFormsModule } from '@angular/forms'; import { ButtonComponent } from '../../shared/components/button/button.component'; import { NgClass, NgStyle, TitleCasePipe } from '@angular/common'; import { BooleanToStringPipe } from '../../shared/pipes/boolean-to-string.pipe'; -import { Subject } from 'rxjs'; +import { Subject, catchError } from 'rxjs'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { EditFormComponent } from '../edit-form/edit-form.component'; import { ChangesAction, DifferenceModalComponent } from '../difference-modal/difference-modal.component'; @@ -27,6 +27,7 @@ import { ToggleButtonComponent } from '../../shared/components/button/toggle-but import { ToastService } from '../../shared/services/toast.service'; import { TestResult } from '../../shared/interfaces/test-result'; import { MatTooltip, MatTooltipModule } from '@angular/material/tooltip'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ selector: 'app-edit-display', @@ -77,6 +78,7 @@ export class EditDisplayComponent { private httpService: HttpService, private helperService: HelperService, private toastService: ToastService, + private errorHandler: ErrorHandling, ) {} showReport(report: Report): void { @@ -95,9 +97,12 @@ export class EditDisplayComponent { rerunReport(): void { const reportId: number = this.report.storageId; - this.httpService.runReport(this.currentView.storageName, reportId).subscribe((response: TestResult): void => { - this.toastService.showSuccess('Report rerun successful'); - this.rerunResult = response; + this.httpService.runReport(this.currentView.storageName, reportId).subscribe({ + next: (response: TestResult): void => { + this.toastService.showSuccess('Report rerun successful'); + this.rerunResult = response; + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -206,12 +211,15 @@ export class EditDisplayComponent { const body = { stub: stubStrategy, ...this.getReportValues(checkpointId) }; - this.httpService.updateReport(storageId, body, this.currentView.storageName).subscribe((response: any) => { - response.report.xml = response.xml; - this.report = response.report; - this.saveReportEvent.next(this.report); - this.editor.setNewReport(this.report.xml); - this.disableEditing(); + this.httpService.updateReport(storageId, body, this.currentView.storageName).subscribe({ + next: (response: any) => { + response.report.xml = response.xml; + this.report = response.report; + this.saveReportEvent.next(this.report); + this.editor.setNewReport(this.report.xml); + this.disableEditing(); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -232,7 +240,9 @@ export class EditDisplayComponent { const data: Record = { [this.currentView.storageName]: [storageId], }; - this.httpService.copyReport(data, 'Test').subscribe(); // TODO: storage is hardcoded, fix issue #196 for this + this.httpService.copyReport(data, 'Test').subscribe({ + error: catchError(this.errorHandler.handleError()), + }); // TODO: storage is hardcoded, fix issue #196 for this } toggleEditMode(value: boolean): void { diff --git a/src/app/shared/classes/error-handling.service.ts b/src/app/shared/classes/error-handling.service.ts new file mode 100644 index 00000000..0a87a1b0 --- /dev/null +++ b/src/app/shared/classes/error-handling.service.ts @@ -0,0 +1,24 @@ +import { HttpErrorResponse } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { ToastService } from '../services/toast.service'; +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class ErrorHandling { + constructor(private toastService: ToastService) {} + + handleError(): (error: HttpErrorResponse) => Observable { + return (error: HttpErrorResponse): Observable => { + const message = error.error; + if (message && message.includes('- detailed error message -')) { + const errorMessageParts = message.split('- detailed error message -'); + this.toastService.showDanger(errorMessageParts[0], errorMessageParts[1]); + } else { + this.toastService.showDanger(error.message, ''); + } + return of(error); + }; + } +} diff --git a/src/app/shared/services/http.service.ts b/src/app/shared/services/http.service.ts index 0d1a79c4..ab334219 100644 --- a/src/app/shared/services/http.service.ts +++ b/src/app/shared/services/http.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; -import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { catchError, map, Observable, of, tap } from 'rxjs'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { map, Observable, tap } from 'rxjs'; import { ToastService } from './toast.service'; import { View } from '../interfaces/view'; import { OptionsSettings } from '../interfaces/options-settings'; @@ -23,36 +23,20 @@ export class HttpService { private toastService: ToastService, ) {} - handleError(): (error: HttpErrorResponse) => Observable { - return (error: HttpErrorResponse): Observable => { - const message = error.error; - if (message.includes('- detailed error message -')) { - const errorMessageParts = message.split('- detailed error message -'); - this.toastService.showDanger(errorMessageParts[0], errorMessageParts[1]); - } else { - this.toastService.showDanger(message, ''); - } - return of(error); - }; - } - handleSuccess(message: string): void { this.toastService.showSuccess(message); } getViews(): Observable { - return this.http - .get('api/testtool/views') - .pipe(catchError(this.handleError())) - .pipe( - map((data: Record) => { - let views: View[] = []; - for (let [key, value] of Object.entries(data)) { - views.push({ ...value, name: key }); - } - return views; - }), - ); + return this.http.get('api/testtool/views').pipe( + map((data: View[]) => { + const views: View[] = []; + for (let [key, value] of Object.entries(data)) { + views.push({ ...value, name: key }); + } + return views; + }), + ); } getMetadataReports( @@ -81,32 +65,29 @@ export class HttpService { } getMetadataCount(storage: string): Observable { - return this.http.get(`api/metadata/${storage}/count`).pipe(catchError(this.handleError())); + return this.http.get(`api/metadata/${storage}/count`); } getLatestReports(amount: number, storage: string): Observable { return this.http .get(`api/report/latest/${storage}/${amount}`) - .pipe(tap(() => this.handleSuccess('Latest' + amount + 'reports opened!'))) - .pipe(catchError(this.handleError())); + .pipe(tap(() => this.handleSuccess('Latest' + amount + 'reports opened!'))); } getReportInProgress(index: number): Observable { return this.http .get(`api/testtool/in-progress/${index}`) - .pipe(tap(() => this.handleSuccess(`Opened report in progress with index [${index}]`))) - .pipe(catchError(this.handleError())); + .pipe(tap(() => this.handleSuccess(`Opened report in progress with index [${index}]`))); } deleteReportInProgress(index: number): Observable { return this.http .delete('api/testtool/in-progress/' + index) - .pipe(tap(() => this.handleSuccess(`Deleted report in progress with index [${index}]`))) - .pipe(catchError(this.handleError())); + .pipe(tap(() => this.handleSuccess(`Deleted report in progress with index [${index}]`))); } getReportsInProgressThresholdTime(): Observable { - return this.http.get('api/testtool/in-progress/threshold-time').pipe(catchError(this.handleError())); + return this.http.get('api/testtool/in-progress/threshold-time'); } getTestReports(metadataNames: string[], storage: string): Observable { @@ -117,47 +98,41 @@ export class HttpService { getReport(reportId: number, storage: string): Observable { return this.http - .get>( - `api/report/${storage}/${reportId}/?xml=true&globalTransformer=${localStorage.getItem('transformationEnabled')}`, - ) + .get< + Record + >(`api/report/${storage}/${reportId}/?xml=true&globalTransformer=${localStorage.getItem('transformationEnabled')}`) .pipe( map((e) => { const report = e['report'] as Report; report.xml = e['xml'] as string; return report; }), - ) - .pipe(catchError(this.handleError())); + ); } getReports(reportIds: number[], storage: string): Observable> { - return this.http - .get< - Record - >(`api/report/${storage}/?xml=true&globalTransformer=${localStorage.getItem('transformationEnabled')}`, { params: { storageIds: reportIds } }) - .pipe(catchError(this.handleError())); + return this.http.get>( + `api/report/${storage}/?xml=true&globalTransformer=${localStorage.getItem('transformationEnabled')}`, + { params: { storageIds: reportIds } }, + ); } updateReport(reportId: string, body: Report, storage: string): Observable { return this.http - .post(`api/report/${storage}/${reportId}`, body) - .pipe(tap(() => this.handleSuccess('Report updated!'))) - .pipe(catchError(this.handleError())); + .post(`api/report/${storage}/${reportId}`, body) + .pipe(tap(() => this.handleSuccess('Report updated!'))); } copyReport(data: Record, storage: string): Observable { return this.http - .put(`api/report/store/${storage}`, data) - .pipe(tap(() => this.handleSuccess('Report copied!'))) - .pipe(catchError(this.handleError())); + .put(`api/report/store/${storage}`, data) + .pipe(tap(() => this.handleSuccess('Report copied!'))); } updatePath(reportIds: number[], storage: string, map: UpdatePathSettings): Observable { - return this.http - .put(`api/report/move/${storage}`, map, { - params: { storageIds: reportIds }, - }) - .pipe(catchError(this.handleError())); + return this.http.put(`api/report/move/${storage}`, map, { + params: { storageIds: reportIds }, + }); } uploadReport(formData: FormData): Observable { @@ -165,100 +140,76 @@ export class HttpService { .post('api/report/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }) - .pipe(tap(() => this.handleSuccess('Report uploaded!'))) - .pipe(catchError(this.handleError())); + .pipe(tap(() => this.handleSuccess('Report uploaded!'))); } uploadReportToStorage(formData: FormData, storage: string): Observable { return this.http - .post(`api/report/upload/${storage}`, formData, { + .post(`api/report/upload/${storage}`, formData, { headers: { 'Content-Type': 'multipart/form-data' }, }) - .pipe(tap(() => this.handleSuccess('Report uploaded to storage!'))) - .pipe(catchError(this.handleError())); + .pipe(tap(() => this.handleSuccess('Report uploaded to storage!'))); } postSettings(settings: UploadParams): Observable { - return this.http - .post('api/testtool', settings) - .pipe(tap(() => this.handleSuccess('Settings saved!'))) - .pipe(catchError(this.handleError())); + return this.http.post('api/testtool', settings).pipe(tap(() => this.handleSuccess('Settings saved!'))); } postTransformation(transformation: string): Observable { - return ( - this.http - .post('api/testtool/transformation', { transformation: transformation }) - // .pipe(tap(() => this.handleSuccess('Transformation saved!'))) - .pipe(catchError(this.handleError())) - ); + return this.http.post('api/testtool/transformation', { transformation: transformation }); + // .pipe(tap(() => this.handleSuccess('Transformation saved!'))) } getTransformation(defaultTransformation: boolean): Observable> { - return this.http - .get>(`api/testtool/transformation/${defaultTransformation}`) - .pipe(catchError(this.handleError())); + return this.http.get>(`api/testtool/transformation/${defaultTransformation}`); } getSettings(): Observable { - return this.http.get('api/testtool').pipe(catchError(this.handleError())); + return this.http.get('api/testtool'); } resetSettings(): Observable { - return this.http.get('api/testtool/reset').pipe(catchError(this.handleError())); + return this.http.get('api/testtool/reset'); } runReport(storage: string, reportId: number): Observable { - return this.http - .post(`api/runner/run/${storage}/${reportId}`, { - headers: this.headers, - observe: 'response', - }) - .pipe(catchError(this.handleError())); + return this.http.post(`api/runner/run/${storage}/${reportId}`, { + headers: this.headers, + observe: 'response', + }); } runDisplayReport(reportId: string, storage: string): Observable { - return this.http - .put(`api/runner/replace/${storage}/${reportId}`, { - headers: this.headers, - observe: 'response', - }) - .pipe(catchError(this.handleError())); + return this.http.put(`api/runner/replace/${storage}/${reportId}`, { + headers: this.headers, + observe: 'response', + }); } cloneReport(storage: string, storageId: number, map: CloneReport): Observable { return this.http - .post(`api/report/move/${storage}/${storageId}`, map) - .pipe(tap(() => this.handleSuccess('Report cloned!'))) - .pipe(catchError(this.handleError())); + .post(`api/report/move/${storage}/${storageId}`, map) + .pipe(tap(() => this.handleSuccess('Report cloned!'))); } deleteReport(reportIds: number[], storage: string): Observable { - return this.http - .delete(`api/report/${storage}`, { params: { storageIds: reportIds } }) - .pipe(catchError(this.handleError())); + return this.http.delete(`api/report/${storage}`, { params: { storageIds: reportIds } }); } replaceReport(reportId: string, storage: string): Observable { - return this.http - .put(`api/runner/replace/${storage}/${reportId}`, { - headers: this.headers, - }) - .pipe(catchError(this.handleError())); + return this.http.put(`api/runner/replace/${storage}/${reportId}`, { + headers: this.headers, + }); } getUnmatchedCheckpoints(storageName: string, storageId: number, viewName: string): Observable { - return this.http - .get(`api/report/${storageName}/${storageId}/checkpoints/uids`, { - params: { view: viewName, invert: true }, - }) - .pipe(catchError(this.handleError())); + return this.http.get(`api/report/${storageName}/${storageId}/checkpoints/uids`, { + params: { view: viewName, invert: true }, + }); } getWarningsAndErrors(storageName: string): Observable { const cleanStorageName = storageName.replaceAll(' ', ''); - return this.http - .get(`api/report/warningsAndErrors/${cleanStorageName}`, { responseType: 'text' }) - .pipe(catchError(this.handleError())); + return this.http.get(`api/report/warningsAndErrors/${cleanStorageName}`, { responseType: 'text' }); } } diff --git a/src/app/test/clone-modal/clone-modal.component.ts b/src/app/test/clone-modal/clone-modal.component.ts index 48de1887..1e7c24f1 100644 --- a/src/app/test/clone-modal/clone-modal.component.ts +++ b/src/app/test/clone-modal/clone-modal.component.ts @@ -4,6 +4,8 @@ import { Report } from '../../shared/interfaces/report'; import { HttpService } from '../../shared/services/http.service'; import { UntypedFormControl, UntypedFormGroup, ReactiveFormsModule } from '@angular/forms'; import { CloneReport } from 'src/app/shared/interfaces/clone-report'; +import { catchError } from 'rxjs'; +import { ErrorHandling } from 'src/app/shared/classes/error-handling.service'; @Component({ selector: 'app-clone-modal', @@ -27,13 +29,17 @@ export class CloneModalComponent { constructor( private modalService: NgbModal, private httpService: HttpService, + private errorHandler: ErrorHandling, ) {} open(selectedReport: any) { - this.httpService.getReport(selectedReport.storageId, this.currentView.storageName).subscribe((report: Report) => { - this.report = report; - this.variableForm.get('message')?.setValue(this.report.inputCheckpoint?.message); - this.modalService.open(this.modal); + this.httpService.getReport(selectedReport.storageId, this.currentView.storageName).subscribe({ + next: (report: Report) => { + this.report = report; + this.variableForm.get('message')?.setValue(this.report.inputCheckpoint?.message); + this.modalService.open(this.modal); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -42,8 +48,9 @@ export class CloneModalComponent { csv: this.variableForm.value.variables, message: this.variableForm.value.message, }; - this.httpService - .cloneReport(this.currentView.storageName, this.report.storageId, map) - .subscribe(() => this.cloneReportEvent.emit()); + this.httpService.cloneReport(this.currentView.storageName, this.report.storageId, map).subscribe({ + next: () => this.cloneReportEvent.emit(), + error: () => catchError(this.errorHandler.handleError()), + }); } } diff --git a/src/app/test/test.component.ts b/src/app/test/test.component.ts index 0d41af0c..a500a757 100644 --- a/src/app/test/test.component.ts +++ b/src/app/test/test.component.ts @@ -19,6 +19,8 @@ import { FormsModule, NgModel, ReactiveFormsModule } from '@angular/forms'; import { ButtonComponent } from '../shared/components/button/button.component'; import { TestListItem } from '../shared/interfaces/test-list-item'; import { View } from '../shared/interfaces/view'; +import { OptionsSettings } from '../shared/interfaces/options-settings'; +import { ErrorHandling } from '../shared/classes/error-handling.service'; export const updatePathActionConst = ['move', 'copy'] as const; export type UpdatePathAction = (typeof updatePathActionConst)[number]; @@ -62,6 +64,7 @@ export class TestComponent implements OnInit, AfterViewInit { private helperService: HelperService, private toastService: ToastService, private tabService: TabService, + private errorHandler: ErrorHandling, ) { this.getGeneratorStatus(); } @@ -96,9 +99,12 @@ export class TestComponent implements OnInit, AfterViewInit { if (generatorStatus) { this.generatorStatus = generatorStatus; } else { - this.httpService.getSettings().subscribe((response) => { - this.generatorStatus = response.generatorEnabled ? 'Enabled' : 'Disabled'; - localStorage.setItem('generatorEnabled', this.generatorStatus); + this.httpService.getSettings().subscribe({ + next: (response: OptionsSettings) => { + this.generatorStatus = response.generatorEnabled ? 'Enabled' : 'Disabled'; + localStorage.setItem('generatorEnabled', this.generatorStatus); + }, + error: () => catchError(this.errorHandler.handleError()), }); } } @@ -116,20 +122,23 @@ export class TestComponent implements OnInit, AfterViewInit { getCopiedReports(): void { this.httpService.getTestReports(this.currentView.metadataNames, this.currentView.storageName).subscribe({ next: (response: TestListItem[]) => this.addCopiedReports(response), - error: () => catchError(this.httpService.handleError()), + error: () => catchError(this.errorHandler.handleError()), }); } loadData(path: string): void { - this.httpService.getViews().subscribe((views: View[]) => { - const defaultView: View | undefined = views.find((view: View) => view.defaultView); - console.log(defaultView); - /*if (defaultView) { - const selectedView: View = views[defaultViewKey]; - if (selectedView.storageName) { - this.currentView.targetStorage = selectedView.storageName; - } - }*/ + this.httpService.getViews().subscribe({ + next: (views: View[]) => { + const defaultView: View | undefined = views.find((view: View) => view.defaultView); + console.log(defaultView); + /*if (defaultView) { + const selectedView: View = views[defaultViewKey]; + if (selectedView.storageName) { + this.currentView.targetStorage = selectedView.storageName; + } + }*/ + }, + error: () => catchError(this.errorHandler.handleError()), }); this.httpService.getTestReports(this.currentView.metadataNames, this.currentView.storageName).subscribe({ next: (value: TestListItem[]) => { @@ -145,7 +154,7 @@ export class TestComponent implements OnInit, AfterViewInit { }); } }, - error: () => catchError(this.httpService.handleError()), + error: () => catchError(this.errorHandler.handleError()), }); } @@ -155,8 +164,9 @@ export class TestComponent implements OnInit, AfterViewInit { run(reportId: number): void { if (this.generatorStatus === 'Enabled') { - this.httpService.runReport(this.currentView.storageName, reportId).subscribe((response: TestResult): void => { - this.showResult(response); + this.httpService.runReport(this.currentView.storageName, reportId).subscribe({ + next: (response: TestResult): void => this.showResult(response), + error: () => catchError(this.errorHandler.handleError()), }); } else { this.toastService.showWarning('Generator is disabled!'); @@ -199,12 +209,15 @@ export class TestComponent implements OnInit, AfterViewInit { } openReport(storageId: number, name: string): void { - this.httpService.getReport(storageId, this.currentView.storageName).subscribe((report: Report): void => { - const reportData: ReportData = { - report: report, - currentView: this.currentView, - }; - this.tabService.openNewTab(reportData); + this.httpService.getReport(storageId, this.currentView.storageName).subscribe({ + next: (report: Report): void => { + const reportData: ReportData = { + report: report, + currentView: this.currentView, + }; + this.tabService.openNewTab(reportData); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -218,8 +231,9 @@ export class TestComponent implements OnInit, AfterViewInit { deleteSelected(): void { this.httpService .deleteReport(this.helperService.getSelectedIds(this.reports), this.currentView.storageName) - .subscribe(() => { - this.loadData(''); + .subscribe({ + next: () => this.loadData(''), + error: () => catchError(this.errorHandler.handleError()), }); } @@ -242,7 +256,10 @@ export class TestComponent implements OnInit, AfterViewInit { if (file) { const formData: FormData = new FormData(); formData.append('file', file); - this.httpService.uploadReportToStorage(formData, this.currentView.storageName).subscribe(() => this.loadData('')); + this.httpService.uploadReportToStorage(formData, this.currentView.storageName).subscribe({ + next: () => this.loadData(''), + error: () => catchError(this.errorHandler.handleError()), + }); } } @@ -264,11 +281,17 @@ export class TestComponent implements OnInit, AfterViewInit { } replaceReport(reportId: string): void { - this.httpService.replaceReport(reportId, this.currentView.targetStorage).subscribe(() => { - this.reranReports = this.reranReports.filter((report: ReranReport) => report.id != reportId); + this.httpService.replaceReport(reportId, this.currentView.targetStorage).subscribe({ + next: () => { + this.reranReports = this.reranReports.filter((report: ReranReport) => report.id != reportId); + }, + error: () => catchError(this.errorHandler.handleError()), }); - this.httpService.replaceReport(reportId, this.currentView.storageName).subscribe(() => { - this.reranReports = this.reranReports.filter((report: ReranReport) => report.id != reportId); + this.httpService.replaceReport(reportId, this.currentView.storageName).subscribe({ + next: () => { + this.reranReports = this.reranReports.filter((report: ReranReport) => report.id != reportId); + }, + error: () => catchError(this.errorHandler.handleError()), }); } @@ -277,8 +300,9 @@ export class TestComponent implements OnInit, AfterViewInit { const data: Record = { [this.currentView.storageName]: copiedIds, }; - this.httpService.copyReport(data, this.currentView.storageName).subscribe(() => { - this.loadData(''); + this.httpService.copyReport(data, this.currentView.storageName).subscribe({ + next: () => this.loadData(''), + error: () => catchError(this.errorHandler.handleError()), }); } @@ -300,7 +324,10 @@ export class TestComponent implements OnInit, AfterViewInit { let path: string = this.moveToInputModel.value; path = this.transformPath(path); const map: UpdatePathSettings = { path: path, action: this.updatePathAction }; - this.httpService.updatePath(reportIds, this.currentView.storageName, map).subscribe(() => this.loadData(path)); + this.httpService.updatePath(reportIds, this.currentView.storageName, map).subscribe({ + next: () => this.loadData(path), + error: () => catchError(this.errorHandler.handleError()), + }); } else { this.toastService.showWarning('No Report Selected!'); }