This is an automated email from the ASF dual-hosted git repository. rfellows pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push: new 8e0c68e5cc NIFI-12663: Error Handling in CS Listing (#8305) 8e0c68e5cc is described below commit 8e0c68e5cc8e39b544ed44a7885d1f04f7ae6f42 Author: Matt Gilman <matt.c.gil...@gmail.com> AuthorDate: Mon Jan 29 15:49:59 2024 -0500 NIFI-12663: Error Handling in CS Listing (#8305) * NIFI-12663: - Handling API error responses in the Management Controller Services page. * NIFI-12679: - Renaming components based on review feedback. - Using ng-content in page-content component. - Removing the problematic route when navigating to the error page. - Fixing logic when handling service loading errors. - Handling errors in the Property Table Helper service. * NIFI-12679: - Addressing review feedback. --- .../src/main/nifi/src/app/app-routing.module.ts | 4 ++ .../src/main/nifi/src/app/app.module.ts | 6 +- .../feature/error-routing.module.ts} | 19 ++--- .../feature/error.component.html} | 13 +++- .../feature/error.component.scss} | 6 +- .../feature/error.component.spec.ts} | 23 ++++-- .../error/feature/error.component.ts} | 27 ++++--- .../error/feature/error.module.ts} | 26 +++---- .../pages/flow-designer/service/flow.service.ts | 4 -- .../port/create-port/create-port.component.html | 2 +- .../port/create-port/create-port.component.ts | 4 +- .../items/port/edit-port/edit-port.component.html | 2 +- .../items/port/edit-port/edit-port.component.ts | 8 +-- .../create-process-group.component.html | 1 + .../create-process-group.component.ts | 4 +- .../group-components/group-components.component.ts | 2 - .../edit-processor/edit-processor.component.html | 1 + .../edit-processor.component.spec.ts | 19 ++++- .../edit-processor/edit-processor.component.ts | 4 +- .../app/pages/login/feature/login.component.html | 16 +++-- .../src/app/pages/login/feature/login.module.ts | 7 +- .../login/ui/login-form/login-form.component.html | 2 +- .../state/management-controller-services/index.ts | 3 +- .../management-controller-services.actions.ts | 4 +- .../management-controller-services.effects.ts | 84 ++++++++++++---------- .../management-controller-services.reducer.ts | 10 +-- .../management-controller-services.selectors.ts | 5 ++ .../management-controller-services.module.ts | 3 +- .../nifi/src/app/service/error-helper.service.ts | 69 ++++++++++++++++++ .../src/app/service/extension-types.service.ts | 2 +- .../src/app/service/guard/authentication.guard.ts | 8 +-- .../service/interceptors/auth.interceptor.spec.ts | 10 ++- .../app/service/interceptors/auth.interceptor.ts | 47 +++++++++--- .../app/service/property-table-helper.service.ts | 53 +++++++++++--- .../error/error.actions.ts} | 20 +++--- .../main/nifi/src/app/state/error/error.effects.ts | 55 ++++++++++++++ .../main/nifi/src/app/state/error/error.reducer.ts | 50 +++++++++++++ .../error/error.selectors.ts} | 18 ++--- .../error/index.ts} | 17 +++-- .../src/main/nifi/src/app/state/index.ts | 4 ++ .../edit-controller-service.component.html | 1 + .../edit-controller-service.component.spec.ts | 19 ++++- .../edit-controller-service.component.ts | 4 +- .../error-banner/error-banner.component.html} | 21 +++--- .../error-banner/error-banner.component.scss} | 14 ++-- .../error-banner/error-banner.component.spec.ts} | 14 ++-- .../common/error-banner/error-banner.component.ts} | 33 ++++----- .../page-content/page-content.component.html} | 10 +-- .../page-content/page-content.component.scss} | 4 +- .../page-content/page-content.component.spec.ts} | 13 ++-- .../common/page-content/page-content.component.ts} | 17 +++-- 51 files changed, 564 insertions(+), 248 deletions(-) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts index 3a70a49d5b..704d6df973 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app-routing.module.ts @@ -24,6 +24,10 @@ const routes: Routes = [ path: 'login', loadChildren: () => import('./pages/login/feature/login.module').then((m) => m.LoginModule) }, + { + path: 'error', + loadChildren: () => import('./pages/error/feature/error.module').then((m) => m.ErrorModule) + }, { path: 'settings', canMatch: [authenticationGuard], diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts index 749a8dda7a..56c94fe9c4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/app.module.ts @@ -42,6 +42,8 @@ import { ControllerServiceStateEffects } from './state/contoller-service-state/c import { SystemDiagnosticsEffects } from './state/system-diagnostics/system-diagnostics.effects'; import { FlowConfigurationEffects } from './state/flow-configuration/flow-configuration.effects'; import { ComponentStateEffects } from './state/component-state/component-state.effects'; +import { ErrorEffects } from './state/error/error.effects'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; @NgModule({ declarations: [AppComponent], @@ -60,6 +62,7 @@ import { ComponentStateEffects } from './state/component-state/component-state.e navigationActionTiming: NavigationActionTiming.PostActivation }), EffectsModule.forRoot( + ErrorEffects, CurrentUserEffects, ExtensionTypesEffects, AboutEffects, @@ -76,7 +79,8 @@ import { ComponentStateEffects } from './state/component-state/component-state.e }), MatProgressSpinnerModule, MatNativeDateModule, - MatDialogModule + MatDialogModule, + MatSnackBarModule ], providers: [ { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error-routing.module.ts similarity index 71% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error-routing.module.ts index 60e6952ab4..110e54ca8c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error-routing.module.ts @@ -15,11 +15,14 @@ * limitations under the License. */ -.login-message { - .login-title { - font-size: 18px; - font-weight: 600; - font-family: Roboto Slab; - color: #728e9b; - } -} +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { Error } from './error.component'; + +const routes: Routes = [{ path: '', component: Error }]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class ErrorRoutingModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.html similarity index 61% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.html rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.html index 7fa5559faf..2dcd49203f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.html @@ -15,4 +15,15 @@ ~ limitations under the License. --> -<div *ngIf="message" [ngClass]="{ error: severity == 'alert' }" class="p-2 text-center text-sm">{{ message }}</div> +<div class="error-background pt-24 pl-24 h-screen"> + <ng-container *ngIf="errorDetail$ | async; let errorDetail; else: noErrorDetails"> + <page-content [title]="errorDetail.title"> + <div class="text-sm">{{ errorDetail.message }}</div> + </page-content> + </ng-container> + <ng-template #noErrorDetails> + <page-content title="Error"> + <div class="text-sm">Please check the logs or navigate home and try again.</div> + </page-content> + </ng-template> +</div> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.scss similarity index 88% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.scss rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.scss index d6751e31d6..ed2e32658f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.scss @@ -15,8 +15,6 @@ * limitations under the License. */ -div { - &.error { - background-color: #ffcdd2; - } +.error-background { + background: #fff url(../../../../assets/icons/bg-error.png) left top no-repeat; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.spec.ts similarity index 70% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.spec.ts index b5c0823056..795a953ec6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.spec.ts @@ -17,20 +17,29 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Banner } from './banner.component'; +import { Error } from './error.component'; import { provideMockStore } from '@ngrx/store/testing'; -import { initialState } from '../../../state/flow/flow.reducer'; +import { initialState } from '../../../state/current-user/current-user.reducer'; +import { Component } from '@angular/core'; -describe('Banner', () => { - let component: Banner; - let fixture: ComponentFixture<Banner>; +describe('Error', () => { + let component: Error; + let fixture: ComponentFixture<Error>; + + @Component({ + selector: 'page-content', + standalone: true, + template: '' + }) + class MockPageContent {} beforeEach(() => { TestBed.configureTestingModule({ - imports: [Banner], + declarations: [Error], + imports: [MockPageContent], providers: [provideMockStore({ initialState })] }); - fixture = TestBed.createComponent(Banner); + fixture = TestBed.createComponent(Error); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.ts similarity index 63% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.ts index 2f45628ff9..2bfa544afd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.component.ts @@ -15,19 +15,18 @@ * limitations under the License. */ -import { TestBed } from '@angular/core/testing'; +import { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { selectFullScreenError } from '../../../state/error/error.selectors'; +import { NiFiState } from '../../../state'; -import { AuthInterceptor } from './auth.interceptor'; +@Component({ + selector: 'error', + templateUrl: './error.component.html', + styleUrls: ['./error.component.scss'] +}) +export class Error { + errorDetail$ = this.store.select(selectFullScreenError); -describe('AuthInterceptor', () => { - let service: AuthInterceptor; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AuthInterceptor); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); + constructor(private store: Store<NiFiState>) {} +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.module.ts similarity index 64% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.module.ts index 2f45628ff9..2e1fb41518 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/error/feature/error.module.ts @@ -15,19 +15,15 @@ * limitations under the License. */ -import { TestBed } from '@angular/core/testing'; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Error } from './error.component'; +import { ErrorRoutingModule } from './error-routing.module'; +import { PageContent } from '../../../ui/common/page-content/page-content.component'; -import { AuthInterceptor } from './auth.interceptor'; - -describe('AuthInterceptor', () => { - let service: AuthInterceptor; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AuthInterceptor); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); +@NgModule({ + declarations: [Error], + exports: [Error], + imports: [CommonModule, ErrorRoutingModule, PageContent] +}) +export class ErrorModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts index 8d841d986c..818e802663 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts @@ -18,7 +18,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -import { CanvasUtils } from './canvas-utils.service'; import { ComponentRunStatusRequest, CreateComponentRequest, @@ -48,7 +47,6 @@ export class FlowService implements PropertyDescriptorRetriever { constructor( private httpClient: HttpClient, - private canvasUtils: CanvasUtils, private client: Client, private nifiCommon: NiFiCommon ) {} @@ -198,12 +196,10 @@ export class FlowService implements PropertyDescriptorRetriever { } updateComponent(updateComponent: UpdateComponentRequest): Observable<any> { - // return throwError('API Error'); return this.httpClient.put(this.nifiCommon.stripProtocol(updateComponent.uri), updateComponent.payload); } deleteComponent(deleteComponent: DeleteComponentRequest): Observable<any> { - // return throwError('API Error'); const revision: any = this.client.getRevision(deleteComponent.entity); return this.httpClient.delete(this.nifiCommon.stripProtocol(deleteComponent.uri), { params: revision }); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.html index 04feabd8f9..b214a166d2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.html @@ -17,7 +17,7 @@ <h2 mat-dialog-title>Create New {{ portTypeLabel }}</h2> <form class="create-port-form" [formGroup]="createPortForm"> - <banner></banner> + <error-banner></error-banner> <mat-dialog-content> <mat-form-field> <mat-label>{{ portTypeLabel }} Name</mat-label> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.ts index eb7a38c31f..926aa98daa 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/create-port/create-port.component.ts @@ -28,7 +28,7 @@ import { ComponentType, SelectOption, TextTipInput } from '../../../../../../../ import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { Banner } from '../../../../common/banner/banner.component'; +import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component'; import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nifi-spinner.directive'; @@ -44,7 +44,7 @@ import { NifiTooltipDirective } from '../../../../../../../ui/common/tooltips/ni MatInputModule, MatSelectModule, MatTooltipModule, - Banner, + ErrorBanner, NgIf, NgForOf, MatButtonModule, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.html index 9bb3faf242..2742b7170c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.html @@ -17,7 +17,7 @@ <h2 mat-dialog-title>Edit {{ portTypeLabel }}</h2> <form class="edit-port-form" [formGroup]="editPortForm"> - <banner></banner> + <error-banner></error-banner> <mat-dialog-content> <div> <mat-form-field> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.ts index 94b68953d1..2fbe452dc4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/port/edit-port/edit-port.component.ts @@ -24,7 +24,7 @@ import { updateComponent } from '../../../../../state/flow/flow.actions'; import { Client } from '../../../../../../../service/client.service'; import { EditComponentDialogRequest } from '../../../../../state/flow'; import { ComponentType } from '../../../../../../../state/shared'; -import { Banner } from '../../../../common/banner/banner.component'; +import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component'; import { MatInputModule } from '@angular/material/input'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; @@ -38,7 +38,7 @@ import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nif templateUrl: './edit-port.component.html', imports: [ ReactiveFormsModule, - Banner, + ErrorBanner, MatDialogModule, MatInputModule, MatCheckboxModule, @@ -68,10 +68,6 @@ export class EditPort { this.portTypeLabel = 'Output Port'; } - // TODO - consider updating the request to only provide the id of the port and selecting that item - // from the store. this would also allow us to be informed when another client has submitted an - // update to the same port which this editing is happening - // build the form this.editPortForm = this.formBuilder.group({ name: new FormControl(request.entity.component.name, Validators.required), diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.html index 6ce3fb0f78..965492caca 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.html @@ -17,6 +17,7 @@ <h2 mat-dialog-title>Create Process Group</h2> <form class="create-process-group-form" [formGroup]="createProcessGroupForm"> + <error-banner></error-banner> <mat-dialog-content> <mat-form-field> <mat-label>Name</mat-label> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.ts index 74b8865aed..2caa01168e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/create-process-group/create-process-group.component.ts @@ -24,7 +24,7 @@ import { createProcessGroup, uploadProcessGroup } from '../../../../../state/flo import { SelectOption, TextTipInput } from '../../../../../../../state/shared'; import { selectSaving } from '../../../../../state/flow/flow.selectors'; import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; -import { Banner } from '../../../../common/banner/banner.component'; +import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @@ -42,7 +42,7 @@ import { NiFiCommon } from '../../../../../../../service/nifi-common.service'; standalone: true, imports: [ AsyncPipe, - Banner, + ErrorBanner, MatButtonModule, MatDialogModule, MatFormFieldModule, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/group-components/group-components.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/group-components/group-components.component.ts index 690f03f3e4..84bd09f637 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/group-components/group-components.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/process-group/group-components/group-components.component.ts @@ -24,7 +24,6 @@ import { groupComponents } from '../../../../../state/flow/flow.actions'; import { ComponentType, SelectOption, TextTipInput } from '../../../../../../../state/shared'; import { selectSaving } from '../../../../../state/flow/flow.selectors'; import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; -import { Banner } from '../../../../common/banner/banner.component'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; @@ -42,7 +41,6 @@ import { Client } from '../../../../../../../service/client.service'; standalone: true, imports: [ AsyncPipe, - Banner, MatButtonModule, MatDialogModule, MatFormFieldModule, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html index 166f54860f..8a67cb245a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html @@ -17,6 +17,7 @@ <h2 mat-dialog-title>Edit Processor</h2> <form class="processor-edit-form" [formGroup]="editProcessorForm"> + <error-banner></error-banner> <!-- TODO - Stop & Configure --> <mat-dialog-content> <mat-tab-group> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.spec.ts index 18db2e5513..c181d773a3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.spec.ts @@ -22,6 +22,9 @@ import { EditComponentDialogRequest } from '../../../../../state/flow'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ComponentType } from '../../../../../../../state/shared'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Component } from '@angular/core'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialState } from '../../../../../../../state/error/error.reducer'; describe('EditProcessor', () => { let component: EditProcessor; @@ -719,10 +722,22 @@ describe('EditProcessor', () => { } }; + @Component({ + selector: 'error-banner', + standalone: true, + template: '' + }) + class MockErrorBanner {} + beforeEach(() => { TestBed.configureTestingModule({ - imports: [EditProcessor, BrowserAnimationsModule], - providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] + imports: [EditProcessor, MockErrorBanner, BrowserAnimationsModule], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: data }, + provideMockStore({ + initialState + }) + ] }); fixture = TestBed.createComponent(EditProcessor); component = fixture.componentInstance; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts index 6cb5893bd7..c494728d35 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts @@ -47,6 +47,7 @@ import { RelationshipConfiguration, RelationshipSettings } from './relationship-settings/relationship-settings.component'; +import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component'; @Component({ selector: 'edit-processor', @@ -68,7 +69,8 @@ import { NifiSpinnerDirective, NifiTooltipDirective, RunDurationSlider, - RelationshipSettings + RelationshipSettings, + ErrorBanner ], styleUrls: ['./edit-processor.component.scss'] }) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.component.html index 81fb3394e3..3f6d3a7f34 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.component.html @@ -23,12 +23,16 @@ <ng-template #loaded> <ng-container *ngIf="access.error; else noErrors"> - <login-message [title]="access.error.title" [message]="access.error.message"></login-message> + <page-content [title]="access.error.title"> + <div class="text-sm">{{ access.error.message }}</div> + </page-content> </ng-container> <ng-template #noErrors> <ng-container *ngIf="access.accessStatus.status === 'ACTIVE'; else needsLogin"> - <login-message [title]="'Success'" [message]="access.accessStatus.message"></login-message> + <page-content [title]="'Success'"> + <div class="text-sm">{{ access.accessStatus.message }}</div> + </page-content> </ng-container> <ng-template #needsLogin> <ng-container *ngIf="access.accessConfig.supportsLogin; else loginNotSupported"> @@ -36,11 +40,9 @@ </ng-container> <ng-template #loginNotSupported> - <login-message - [title]="'Access Denied'" - [message]=" - 'This NiFi is not configured to support username/password logins.' - "></login-message> + <page-content [title]="'Access Denied'"> + <div class="text-sm">This NiFi is not configured to support username/password logins.</div> + </page-content> </ng-template> </ng-template> </ng-template> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.module.ts index 339c933ede..9d317caa47 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/feature/login.module.ts @@ -28,11 +28,11 @@ import { EffectsModule } from '@ngrx/effects'; import { loginFeatureKey, reducers } from '../state'; import { AccessEffects } from '../state/access/access.effects'; import { LoginForm } from '../ui/login-form/login-form.component'; -import { LoginMessage } from '../ui/login-message/login-message.component'; +import { PageContent } from '../../../ui/common/page-content/page-content.component'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @NgModule({ - declarations: [Login, LoginForm, LoginMessage], + declarations: [Login, LoginForm], exports: [Login], imports: [ CommonModule, @@ -44,7 +44,8 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; MatFormFieldModule, MatInputModule, MatButtonModule, - NgxSkeletonLoaderModule + NgxSkeletonLoaderModule, + PageContent ] }) export class LoginModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-form/login-form.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-form/login-form.component.html index bad2efb042..7f5d6dc18e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-form/login-form.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-form/login-form.component.html @@ -15,7 +15,7 @@ ~ limitations under the License. --> -<div class="login-form w-96 flex flex-col gap-y-3"> +<div class="login-form w-96 flex flex-col gap-y-5"> <div class="flex justify-between items-center"> <div class="login-title">Log In</div> <div class="flex gap-x-2"> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts index b1725d338c..6c1be51d41 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts @@ -57,6 +57,5 @@ export interface ManagementControllerServicesState { controllerServices: ControllerServiceEntity[]; saving: boolean; loadedTimestamp: string; - error: string | null; - status: 'pending' | 'loading' | 'error' | 'success'; + status: 'pending' | 'loading' | 'success'; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts index 0ecaca9f51..483b481bf4 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts @@ -45,8 +45,8 @@ export const loadManagementControllerServicesSuccess = createAction( props<{ response: LoadManagementControllerServicesResponse }>() ); -export const managementControllerServicesApiError = createAction( - '[Management Controller Services] Load Management Controller Services Error', +export const managementControllerServicesBannerApiError = createAction( + '[Management Controller Services] Management Controller Services Banner Api Error', props<{ error: string }>() ); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts index 62abf521cb..b94109c269 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts @@ -18,7 +18,8 @@ import { Injectable } from '@angular/core'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import * as ManagementControllerServicesActions from './management-controller-services.actions'; -import { catchError, from, map, NEVER, Observable, of, switchMap, take, takeUntil, tap } from 'rxjs'; +import * as ErrorActions from '../../../../state/error/error.actions'; +import { catchError, from, map, of, switchMap, take, takeUntil, tap } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; import { Store } from '@ngrx/store'; @@ -31,17 +32,15 @@ import { EditControllerService } from '../../../../ui/common/controller-service/ import { ComponentType, ControllerServiceReferencingComponent, - InlineServiceCreationRequest, - InlineServiceCreationResponse, - PropertyDescriptor, UpdateControllerServiceRequest } from '../../../../state/shared'; import { Router } from '@angular/router'; -import { ExtensionTypesService } from '../../../../service/extension-types.service'; -import { selectSaving } from './management-controller-services.selectors'; +import { selectSaving, selectStatus } from './management-controller-services.selectors'; import { EnableControllerService } from '../../../../ui/common/controller-service/enable-controller-service/enable-controller-service.component'; import { DisableControllerService } from '../../../../ui/common/controller-service/disable-controller-service/disable-controller-service.component'; import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { ErrorHelper } from '../../../../service/error-helper.service'; @Injectable() export class ManagementControllerServicesEffects { @@ -50,7 +49,7 @@ export class ManagementControllerServicesEffects { private store: Store<NiFiState>, private client: Client, private managementControllerServiceService: ManagementControllerServiceService, - private extensionTypesService: ExtensionTypesService, + private errorHelper: ErrorHelper, private dialog: MatDialog, private router: Router, private propertyTableHelperService: PropertyTableHelperService @@ -59,7 +58,8 @@ export class ManagementControllerServicesEffects { loadManagementControllerServices$ = createEffect(() => this.actions$.pipe( ofType(ManagementControllerServicesActions.loadManagementControllerServices), - switchMap(() => + concatLatestFrom(() => this.store.select(selectStatus)), + switchMap(([action, status]) => from(this.managementControllerServiceService.getControllerServices()).pipe( map((response) => ManagementControllerServicesActions.loadManagementControllerServicesSuccess({ @@ -69,13 +69,17 @@ export class ManagementControllerServicesEffects { } }) ), - catchError((error) => - of( - ManagementControllerServicesActions.managementControllerServicesApiError({ - error: error.error - }) - ) - ) + catchError((errorResponse: HttpErrorResponse) => { + if (status === 'success') { + if (this.errorHelper.showErrorInContext(errorResponse.status)) { + return of(ErrorActions.snackBarError({ error: errorResponse.error })); + } else { + return of(this.errorHelper.fullScreenError(errorResponse)); + } + } else { + return of(this.errorHelper.fullScreenError(errorResponse)); + } + }) ) ) ) @@ -130,13 +134,10 @@ export class ManagementControllerServicesEffects { } }) ), - catchError((error) => - of( - ManagementControllerServicesActions.managementControllerServicesApiError({ - error: error.error - }) - ) - ) + catchError((errorResponse: HttpErrorResponse) => { + this.dialog.closeAll(); + return of(ErrorActions.snackBarError({ error: errorResponse.error })); + }) ) ) ) @@ -265,6 +266,8 @@ export class ManagementControllerServicesEffects { }); editDialogReference.afterClosed().subscribe((response) => { + this.store.dispatch(ErrorActions.clearBannerErrors()); + if (response != 'ROUTED') { this.store.dispatch( ManagementControllerServicesActions.selectControllerService({ @@ -295,18 +298,31 @@ export class ManagementControllerServicesEffects { } }) ), - catchError((error) => - of( - ManagementControllerServicesActions.managementControllerServicesApiError({ - error: error.error - }) - ) - ) + catchError((errorResponse: HttpErrorResponse) => { + if (this.errorHelper.showErrorInContext(errorResponse.status)) { + return of( + ManagementControllerServicesActions.managementControllerServicesBannerApiError({ + error: errorResponse.error + }) + ); + } else { + this.dialog.getDialogById(request.id)?.close('ROUTED'); + return of(this.errorHelper.fullScreenError(errorResponse)); + } + }) ) ) ) ); + managementControllerServicesBannerApiError$ = createEffect(() => + this.actions$.pipe( + ofType(ManagementControllerServicesActions.managementControllerServicesBannerApiError), + map((action) => action.error), + switchMap((error) => of(ErrorActions.addBannerError({ error }))) + ) + ); + configureControllerServiceSuccess$ = createEffect( () => this.actions$.pipe( @@ -425,13 +441,9 @@ export class ManagementControllerServicesEffects { } }) ), - catchError((error) => - of( - ManagementControllerServicesActions.managementControllerServicesApiError({ - error: error.error - }) - ) - ) + catchError((errorResponse: HttpErrorResponse) => { + return of(ErrorActions.snackBarError({ error: errorResponse.error })); + }) ) ) ) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.reducer.ts index c23194a49b..f276f37632 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.reducer.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.reducer.ts @@ -27,7 +27,7 @@ import { inlineCreateControllerServiceSuccess, loadManagementControllerServices, loadManagementControllerServicesSuccess, - managementControllerServicesApiError, + managementControllerServicesBannerApiError, resetManagementControllerServicesState } from './management-controller-services.actions'; import { produce } from 'immer'; @@ -36,7 +36,6 @@ export const initialState: ManagementControllerServicesState = { controllerServices: [], saving: false, loadedTimestamp: '', - error: null, status: 'pending' }; @@ -53,14 +52,11 @@ export const managementControllerServicesReducer = createReducer( ...state, controllerServices: response.controllerServices, loadedTimestamp: response.loadedTimestamp, - error: null, status: 'success' as const })), - on(managementControllerServicesApiError, (state, { error }) => ({ + on(managementControllerServicesBannerApiError, (state, { error }) => ({ ...state, - saving: false, - error, - status: 'error' as const + saving: false })), on(createControllerService, configureControllerService, deleteControllerService, (state, { request }) => ({ ...state, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.selectors.ts index 38a6849574..dccee8af0b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.selectors.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.selectors.ts @@ -31,6 +31,11 @@ export const selectSaving = createSelector( (state: ManagementControllerServicesState) => state.saving ); +export const selectStatus = createSelector( + selectManagementControllerServicesState, + (state: ManagementControllerServicesState) => state.status +); + export const selectControllerServiceIdFromRoute = createSelector(selectCurrentRoute, (route) => { if (route) { // always select the controller service from the route diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.module.ts index 15fc85f780..815f85abb2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/management-controller-services/management-controller-services.module.ts @@ -20,10 +20,11 @@ import { CommonModule } from '@angular/common'; import { ManagementControllerServices } from './management-controller-services.component'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { ControllerServiceTable } from '../../../../ui/common/controller-service/controller-service-table/controller-service-table.component'; +import { ErrorBanner } from '../../../../ui/common/error-banner/error-banner.component'; @NgModule({ declarations: [ManagementControllerServices], exports: [ManagementControllerServices], - imports: [CommonModule, NgxSkeletonLoaderModule, ControllerServiceTable] + imports: [CommonModule, NgxSkeletonLoaderModule, ControllerServiceTable, ErrorBanner] }) export class ManagementControllerServicesModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/error-helper.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/error-helper.service.ts new file mode 100644 index 0000000000..3b1ef875b1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/error-helper.service.ts @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import * as ErrorDetailActions from '../state/error/error.actions'; +import { Action } from '@ngrx/store'; +import { NiFiCommon } from './nifi-common.service'; + +@Injectable({ providedIn: 'root' }) +export class ErrorHelper { + constructor(private nifiCommon: NiFiCommon) {} + + fullScreenError(errorResponse: HttpErrorResponse): Action { + let title: string; + let message: string; + + switch (errorResponse.status) { + case 401: + title = 'Unauthorized'; + break; + case 403: + title = 'Insufficient Permissions'; + break; + case 409: + title = 'Invalid State'; + break; + case 413: + title = 'Payload Too Large'; + break; + case 503: + default: + title = 'An unexpected error has occurred'; + break; + } + + if (this.nifiCommon.isBlank(errorResponse.error)) { + message = + 'An error occurred communicating with NiFi. Please check the logs and fix any configuration issues before restarting.'; + } else { + message = errorResponse.error; + } + + return ErrorDetailActions.fullScreenError({ + errorDetail: { + title, + message + } + }); + } + + showErrorInContext(status: number): boolean { + return [400, 403, 404, 409, 413, 503].includes(status); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/extension-types.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/extension-types.service.ts index 2eced9f549..5fbc01928a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/extension-types.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/extension-types.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { Bundle } from '../state/shared'; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/guard/authentication.guard.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/guard/authentication.guard.ts index 7cf5227f6c..bdd3a16388 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/guard/authentication.guard.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/guard/authentication.guard.ts @@ -80,7 +80,9 @@ export const authenticationGuard: CanMatchFn = (route, state) => { .select(selectCurrentUserState) .pipe(take(1)) .subscribe((userState) => { - if (userState.status == 'pending') { + if (userState.status == 'success') { + resolve(true); + } else { userService .getUser() .pipe(take(1)) @@ -118,7 +120,7 @@ export const authenticationGuard: CanMatchFn = (route, state) => { } }, error: (error) => { - // there is no anonymous access and we don't know this user - open the login page which handles login/registration/etc + // there is no anonymous access and we don't know this user - open the login page which handles login if (error.status === 401) { authStorage.removeToken(); window.location.href = './login'; @@ -126,8 +128,6 @@ export const authenticationGuard: CanMatchFn = (route, state) => { resolve(false); } }); - } else { - resolve(true); } }); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts index 2f45628ff9..1ce007609f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts @@ -18,12 +18,20 @@ import { TestBed } from '@angular/core/testing'; import { AuthInterceptor } from './auth.interceptor'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialState } from '../../state/error/error.reducer'; describe('AuthInterceptor', () => { let service: AuthInterceptor; beforeEach(() => { - TestBed.configureTestingModule({}); + TestBed.configureTestingModule({ + providers: [ + provideMockStore({ + initialState + }) + ] + }); service = TestBed.inject(AuthInterceptor); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.ts index bc07793436..7c7620ca33 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.ts @@ -19,25 +19,54 @@ import { Injectable } from '@angular/core'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable, tap } from 'rxjs'; import { AuthStorage } from '../auth-storage.service'; +import { Store } from '@ngrx/store'; +import { NiFiState } from '../../state'; +import { fullScreenError } from '../../state/error/error.actions'; +import { NiFiCommon } from '../nifi-common.service'; @Injectable({ providedIn: 'root' }) export class AuthInterceptor implements HttpInterceptor { - constructor(private authStorage: AuthStorage) {} + routedToFullScreenError: boolean = false; + + constructor( + private authStorage: AuthStorage, + private store: Store<NiFiState>, + private nifiCommon: NiFiCommon + ) {} intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request).pipe( tap({ - error: (error) => { - if (error instanceof HttpErrorResponse) { - if (error.status === 401) { - this.authStorage.removeToken(); + error: (errorResponse) => { + if (errorResponse instanceof HttpErrorResponse) { + if (errorResponse.status === 401) { + if (this.authStorage.hasToken()) { + this.authStorage.removeToken(); + + let message: string = errorResponse.error; + if (this.nifiCommon.isBlank(message)) { + message = 'Your session has expired. Please navigate home to log in again.'; + } else { + message += '. Please navigate home to log in again.'; + } + + this.routedToFullScreenError = true; - // navigate to the root of the app which will handle redirection to the - // login form if appropriate... TODO - replace with logout complete page? - window.location.href = './login'; - } // TODO handle others (403) + this.store.dispatch( + fullScreenError({ + errorDetail: { + title: 'Unauthorized', + message + } + }) + ); + } else if (!this.routedToFullScreenError) { + // the user has never logged in, redirect them to do so + window.location.href = './login'; + } + } } } }) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts index c359980e6f..d46068ca9f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts @@ -17,7 +17,7 @@ import { Injectable } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { catchError, map, NEVER, Observable, switchMap, take } from 'rxjs'; +import { catchError, EMPTY, map, Observable, switchMap, take, takeUntil, tap } from 'rxjs'; import { ControllerServiceCreator, ControllerServiceEntity, @@ -32,9 +32,12 @@ import { } from '../state/shared'; import { NewPropertyDialog } from '../ui/common/new-property-dialog/new-property-dialog.component'; import { CreateControllerService } from '../ui/common/controller-service/create-controller-service/create-controller-service.component'; -import { ManagementControllerServiceService } from '../pages/settings/service/management-controller-service.service'; import { ExtensionTypesService } from './extension-types.service'; import { Client } from './client.service'; +import { NiFiState } from '../state'; +import { Store } from '@ngrx/store'; +import { snackBarError } from '../state/error/error.actions'; +import { HttpErrorResponse } from '@angular/common/http'; @Injectable({ providedIn: 'root' @@ -42,6 +45,7 @@ import { Client } from './client.service'; export class PropertyTableHelperService { constructor( private dialog: MatDialog, + private store: Store<NiFiState>, private extensionTypesService: ExtensionTypesService, private client: Client ) {} @@ -63,12 +67,19 @@ export class PropertyTableHelperService { }); return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), + takeUntil(newPropertyDialogReference.afterClosed()), switchMap((dialogResponse: NewPropertyDialogResponse) => { return propertyDescriptorService .getPropertyDescriptor(id, dialogResponse.name, dialogResponse.sensitive) .pipe( take(1), + catchError((errorResponse: HttpErrorResponse) => { + this.store.dispatch(snackBarError({ error: errorResponse.error })); + + // handle the error here to keep the observable alive so the + // user can attempt to create the property again + return EMPTY; + }), map((response) => { newPropertyDialogReference.close(); @@ -112,6 +123,11 @@ export class PropertyTableHelperService { ) .pipe( take(1), + tap({ + error: (errorResponse: HttpErrorResponse) => { + this.store.dispatch(snackBarError({ error: errorResponse.error })); + } + }), switchMap((implementingTypesResponse) => { // show the create controller service dialog with the types that implemented the interface const createServiceDialogReference = this.dialog.open(CreateControllerService, { @@ -122,7 +138,7 @@ export class PropertyTableHelperService { }); return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), + takeUntil(createServiceDialogReference.afterClosed()), switchMap((controllerServiceType) => { // typically this sequence would be implemented with ngrx actions, however we are // currently in an edit session, and we need to return both the value (new service id) @@ -142,6 +158,17 @@ export class PropertyTableHelperService { return controllerServiceCreator.createControllerService(payload).pipe( take(1), + catchError((errorResponse: HttpErrorResponse) => { + this.store.dispatch( + snackBarError({ + error: `Unable to create new Service: ${errorResponse.error}` + }) + ); + + // handle the error here to keep the observable alive so the + // user can attempt to create the service again + return EMPTY; + }), switchMap((createResponse) => { // if provided, call the callback function if (afterServiceCreated) { @@ -153,6 +180,20 @@ export class PropertyTableHelperService { .getPropertyDescriptor(id, descriptor.name, false) .pipe( take(1), + tap({ + error: (errorResponse: HttpErrorResponse) => { + // we've errored getting the descriptor but since the service + // was already created, we should close the create service dialog + // so multiple service instances are not inadvertently created + createServiceDialogReference.close(); + + this.store.dispatch( + snackBarError({ + error: `Service created but unable to reload Property Descriptor: ${errorResponse.error}` + }) + ); + } + }), map((descriptorResponse) => { createServiceDialogReference.close(); @@ -162,10 +203,6 @@ export class PropertyTableHelperService { }; }) ); - }), - catchError((error) => { - // TODO - show error - return NEVER; }) ); }) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.actions.ts similarity index 59% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.actions.ts index 2f45628ff9..6278eb92fc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.actions.ts @@ -15,19 +15,15 @@ * limitations under the License. */ -import { TestBed } from '@angular/core/testing'; +import { createAction, props } from '@ngrx/store'; +import { ErrorDetail } from './index'; -import { AuthInterceptor } from './auth.interceptor'; +export const fullScreenError = createAction('[Error] Full Screen Error', props<{ errorDetail: ErrorDetail }>()); -describe('AuthInterceptor', () => { - let service: AuthInterceptor; +export const snackBarError = createAction('[Error] Snackbar Error', props<{ error: string }>()); - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AuthInterceptor); - }); +export const addBannerError = createAction('[Error] Add Banner Error', props<{ error: string }>()); - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); +export const clearBannerErrors = createAction('[Error] Clear Banner Errors'); + +export const resetErrorState = createAction('[Error] Reset Error State'); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.effects.ts new file mode 100644 index 0000000000..4a4d48e3a1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.effects.ts @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { Actions, createEffect, ofType } from '@ngrx/effects'; +import * as ErrorActions from './error.actions'; +import { map, tap } from 'rxjs'; +import { Router } from '@angular/router'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +@Injectable() +export class ErrorEffects { + constructor( + private actions$: Actions, + private router: Router, + private snackBar: MatSnackBar + ) {} + + fullScreenError$ = createEffect( + () => + this.actions$.pipe( + ofType(ErrorActions.fullScreenError), + tap(() => { + this.router.navigate(['/error'], { replaceUrl: true }); + }) + ), + { dispatch: false } + ); + + snackBarError$ = createEffect( + () => + this.actions$.pipe( + ofType(ErrorActions.snackBarError), + map((action) => action.error), + tap((error) => { + this.snackBar.open(error, 'Dismiss', { duration: 30000 }); + }) + ), + { dispatch: false } + ); +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.reducer.ts new file mode 100644 index 0000000000..939be0688d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.reducer.ts @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createReducer, on } from '@ngrx/store'; +import { ErrorState } from './index'; +import { resetErrorState, fullScreenError, addBannerError, clearBannerErrors } from './error.actions'; +import { produce } from 'immer'; + +export const initialState: ErrorState = { + bannerErrors: null, + fullScreenError: null +}; + +export const errorReducer = createReducer( + initialState, + on(fullScreenError, (state, { errorDetail }) => ({ + ...state, + fullScreenError: errorDetail + })), + on(addBannerError, (state, { error }) => { + return produce(state, (draftState) => { + if (draftState.bannerErrors === null) { + draftState.bannerErrors = []; + } + + draftState.bannerErrors.push(error); + }); + }), + on(clearBannerErrors, (state) => ({ + ...state, + bannerErrors: null + })), + on(resetErrorState, (state) => ({ + ...initialState + })) +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.selectors.ts similarity index 64% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.selectors.ts index 2f45628ff9..0887a5d3de 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/interceptors/auth.interceptor.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/error.selectors.ts @@ -15,19 +15,11 @@ * limitations under the License. */ -import { TestBed } from '@angular/core/testing'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { errorFeatureKey, ErrorState } from './index'; -import { AuthInterceptor } from './auth.interceptor'; +export const selectErrorState = createFeatureSelector<ErrorState>(errorFeatureKey); -describe('AuthInterceptor', () => { - let service: AuthInterceptor; +export const selectFullScreenError = createSelector(selectErrorState, (state: ErrorState) => state.fullScreenError); - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AuthInterceptor); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); +export const selectBannerErrors = createSelector(selectErrorState, (state: ErrorState) => state.bannerErrors); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/index.ts similarity index 78% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/index.ts index 60e6952ab4..b4febc851b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/error/index.ts @@ -15,11 +15,14 @@ * limitations under the License. */ -.login-message { - .login-title { - font-size: 18px; - font-weight: 600; - font-family: Roboto Slab; - color: #728e9b; - } +export const errorFeatureKey = 'error'; + +export interface ErrorDetail { + title: string; + message: string; +} + +export interface ErrorState { + bannerErrors: string[] | null; + fullScreenError: ErrorDetail | null; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts index 4ae6619448..ed546d4a04 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/index.ts @@ -33,9 +33,12 @@ import { flowConfigurationFeatureKey, FlowConfigurationState } from './flow-conf import { flowConfigurationReducer } from './flow-configuration/flow-configuration.reducer'; import { componentStateFeatureKey, ComponentStateState } from './component-state'; import { componentStateReducer } from './component-state/component-state.reducer'; +import { errorFeatureKey, ErrorState } from './error'; +import { errorReducer } from './error/error.reducer'; export interface NiFiState { router: RouterReducerState; + [errorFeatureKey]: ErrorState; [currentUserFeatureKey]: CurrentUserState; [extensionTypesFeatureKey]: ExtensionTypesState; [aboutFeatureKey]: AboutState; @@ -48,6 +51,7 @@ export interface NiFiState { export const rootReducers: ActionReducerMap<NiFiState> = { router: routerReducer, + [errorFeatureKey]: errorReducer, [currentUserFeatureKey]: currentUserReducer, [extensionTypesFeatureKey]: extensionTypesReducer, [aboutFeatureKey]: aboutReducer, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.html index b507ec9201..b30e3f85fa 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.html @@ -17,6 +17,7 @@ <h2 mat-dialog-title>Edit Controller Service</h2> <form class="controller-service-edit-form" [formGroup]="editControllerServiceForm"> + <error-banner></error-banner> <mat-dialog-content> <mat-tab-group> <mat-tab label="Settings"> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.spec.ts index d9f5385b2f..750f179e34 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.spec.ts @@ -21,6 +21,9 @@ import { EditControllerService } from './edit-controller-service.component'; import { EditControllerServiceDialogRequest } from '../../../../state/shared'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Component } from '@angular/core'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialState } from '../../../../state/error/error.reducer'; describe('EditControllerService', () => { let component: EditControllerService; @@ -541,10 +544,22 @@ describe('EditControllerService', () => { } }; + @Component({ + selector: 'error-banner', + standalone: true, + template: '' + }) + class MockErrorBanner {} + beforeEach(() => { TestBed.configureTestingModule({ - imports: [EditControllerService, BrowserAnimationsModule], - providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] + imports: [EditControllerService, MockErrorBanner, BrowserAnimationsModule], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: data }, + provideMockStore({ + initialState + }) + ] }); fixture = TestBed.createComponent(EditControllerService); component = fixture.componentInstance; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.ts index 08173288e6..347ace63ee 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/edit-controller-service/edit-controller-service.component.ts @@ -43,6 +43,7 @@ import { ControllerServiceApi } from '../controller-service-api/controller-servi import { Observable } from 'rxjs'; import { ControllerServiceReferences } from '../controller-service-references/controller-service-references.component'; import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive'; +import { ErrorBanner } from '../../error-banner/error-banner.component'; @Component({ selector: 'edit-controller-service', @@ -63,7 +64,8 @@ import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive'; ControllerServiceApi, ControllerServiceReferences, AsyncPipe, - NifiSpinnerDirective + NifiSpinnerDirective, + ErrorBanner ], styleUrls: ['./edit-controller-service.component.scss'] }) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.html similarity index 58% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.html index 98ff4032ed..ef9cb6a0d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.html @@ -15,13 +15,18 @@ ~ limitations under the License. --> -<div class="login-message w-96 flex flex-col gap-y-3"> - <div class="flex justify-between items-center"> - <div class="login-title">{{ title }}</div> - <div class="flex gap-x-3"> - <a (click)="logout()" *ngIf="hasToken()">log out</a> - <a [routerLink]="['/']">home</a> +<ng-container *ngIf="(messages$ | async)!; let messages"> + <div class="banner-container border-t border-b px-6 py-3 flex justify-between items-center"> + <ng-container *ngIf="messages.length === 1; else multipleMessages"> + <div>{{ messages[0] }}</div> + </ng-container> + <ng-template #multipleMessages> + <ul> + <li *ngFor="let message of messages">{{ message }}</li> + </ul> + </ng-template> + <div class="flex flex-col mt-auto"> + <button mat-stroked-button (click)="dismiss()">Dismiss</button> </div> </div> - <div class="text-sm">{{ message }}</div> -</div> +</ng-container> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.scss similarity index 82% copy from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss copy to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.scss index 60e6952ab4..6f854b7380 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.scss @@ -15,11 +15,13 @@ * limitations under the License. */ -.login-message { - .login-title { - font-size: 18px; - font-weight: 600; - font-family: Roboto Slab; - color: #728e9b; +@use '@angular/material' as mat; + +.banner-container { + @include mat.button-density(-1); + + ul { + list-style-type: disc; + list-style-position: inside; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.spec.ts similarity index 79% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.spec.ts index b5c0823056..b660e829d3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.spec.ts @@ -17,20 +17,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { Banner } from './banner.component'; +import { ErrorBanner } from './error-banner.component'; import { provideMockStore } from '@ngrx/store/testing'; -import { initialState } from '../../../state/flow/flow.reducer'; +import { initialState } from '../../../state/error/error.reducer'; -describe('Banner', () => { - let component: Banner; - let fixture: ComponentFixture<Banner>; +describe('ErrorBanner', () => { + let component: ErrorBanner; + let fixture: ComponentFixture<ErrorBanner>; beforeEach(() => { TestBed.configureTestingModule({ - imports: [Banner], + imports: [ErrorBanner], providers: [provideMockStore({ initialState })] }); - fixture = TestBed.createComponent(Banner); + fixture = TestBed.createComponent(ErrorBanner); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.ts similarity index 56% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.ts rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.ts index cb42788943..27b0672866 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/common/banner/banner.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/error-banner/error-banner.component.ts @@ -17,28 +17,25 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; -import { CanvasState } from '../../../state'; -import { selectApiError } from '../../../state/flow/flow.selectors'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { NgClass, NgIf } from '@angular/common'; +import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; +import { MatButtonModule } from '@angular/material/button'; +import { NiFiState } from '../../../state'; +import { selectBannerErrors } from '../../../state/error/error.selectors'; +import { clearBannerErrors } from '../../../state/error/error.actions'; @Component({ - selector: 'banner', + selector: 'error-banner', standalone: true, - imports: [NgClass, NgIf], - templateUrl: './banner.component.html', - styleUrls: ['./banner.component.scss'] + imports: [NgIf, NgForOf, MatButtonModule, AsyncPipe], + templateUrl: './error-banner.component.html', + styleUrls: ['./error-banner.component.scss'] }) -export class Banner { - message: string | null = ''; - severity: string = 'alert'; +export class ErrorBanner { + messages$ = this.store.select(selectBannerErrors); - constructor(private store: Store<CanvasState>) { - this.store - .select(selectApiError) - .pipe(takeUntilDestroyed()) - .subscribe((message) => { - this.message = message; - }); + constructor(private store: Store<NiFiState>) {} + + dismiss(): void { + this.store.dispatch(clearBannerErrors()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.html similarity index 73% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.html index 98ff4032ed..594b0d3c31 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.html @@ -15,13 +15,13 @@ ~ limitations under the License. --> -<div class="login-message w-96 flex flex-col gap-y-3"> - <div class="flex justify-between items-center"> - <div class="login-title">{{ title }}</div> +<div class="page-content w-1/2 flex flex-col gap-y-5"> + <div class="flex justify-between items-center gap-x-3"> + <div class="title whitespace-nowrap overflow-hidden text-ellipsis" [title]="title">{{ title }}</div> <div class="flex gap-x-3"> - <a (click)="logout()" *ngIf="hasToken()">log out</a> + <a (click)="logout()" *ngIf="hasToken()" class="whitespace-nowrap">log out</a> <a [routerLink]="['/']">home</a> </div> </div> - <div class="text-sm">{{ message }}</div> + <ng-content></ng-content> </div> diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.scss similarity index 96% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.scss index 60e6952ab4..634d984516 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.scss @@ -15,8 +15,8 @@ * limitations under the License. */ -.login-message { - .login-title { +.page-content { + .title { font-size: 18px; font-weight: 600; font-family: Roboto Slab; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.spec.ts similarity index 78% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.spec.ts rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.spec.ts index 938d6f4b31..b61aeaa042 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.spec.ts @@ -17,21 +17,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { LoginMessage } from './login-message.component'; +import { PageContent } from './page-content.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { RouterModule } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -describe('LoginMessage', () => { - let component: LoginMessage; - let fixture: ComponentFixture<LoginMessage>; +describe('PageContent', () => { + let component: PageContent; + let fixture: ComponentFixture<PageContent>; beforeEach(() => { TestBed.configureTestingModule({ - declarations: [LoginMessage], - imports: [HttpClientTestingModule, RouterModule, RouterTestingModule] + imports: [PageContent, HttpClientTestingModule, RouterModule, RouterTestingModule] }); - fixture = TestBed.createComponent(LoginMessage); + fixture = TestBed.createComponent(PageContent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.ts similarity index 73% rename from nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.ts rename to nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.ts index 3468db13f5..d0c965f77a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/login/ui/login-message/login-message.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/page-content/page-content.component.ts @@ -16,17 +16,20 @@ */ import { Component, Input } from '@angular/core'; -import { AuthStorage } from '../../../../service/auth-storage.service'; -import { AuthService } from '../../../../service/auth.service'; +import { AuthStorage } from '../../../service/auth-storage.service'; +import { AuthService } from '../../../service/auth.service'; +import { RouterLink } from '@angular/router'; +import { NgIf } from '@angular/common'; @Component({ - selector: 'login-message', - templateUrl: './login-message.component.html', - styleUrls: ['./login-message.component.scss'] + selector: 'page-content', + standalone: true, + templateUrl: './page-content.component.html', + imports: [RouterLink, NgIf], + styleUrls: ['./page-content.component.scss'] }) -export class LoginMessage { +export class PageContent { @Input() title: string = ''; - @Input() message: string = ''; constructor( private authStorage: AuthStorage,