mirror of
https://github.com/apache/nifi.git
synced 2025-03-03 16:09:19 +00:00
[NIFI-13876] - Adding context aware error banners (#9396)
This commit is contained in:
parent
1823a52e36
commit
a53e0ab81e
@ -29,16 +29,16 @@ import { AccessPolicyService } from '../../service/access-policy.service';
|
||||
import { AccessPolicyEntity, ComponentResourceAction, PolicyStatus, ResourceAction } from '../shared';
|
||||
import { selectAccessPolicy, selectResourceAction, selectSaving } from './access-policy.selectors';
|
||||
import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
|
||||
import { isDefinedAndNotNull } from 'libs/shared/src';
|
||||
import { isDefinedAndNotNull, MEDIUM_DIALOG, SMALL_DIALOG } from 'libs/shared/src';
|
||||
import { TenantEntity } from '../../../../state/shared';
|
||||
import { AddTenantToPolicyDialog } from '../../ui/common/add-tenant-to-policy-dialog/add-tenant-to-policy-dialog.component';
|
||||
import { AddTenantsToPolicyRequest } from './index';
|
||||
import { selectUserGroups, selectUsers } from '../tenants/tenants.selectors';
|
||||
import { OverridePolicyDialog } from '../../ui/common/override-policy-dialog/override-policy-dialog.component';
|
||||
import { MEDIUM_DIALOG, SMALL_DIALOG } from 'libs/shared/src';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { loadCurrentUser } from '../../../../state/current-user/current-user.actions';
|
||||
import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class AccessPolicyEffects {
|
||||
@ -495,7 +495,16 @@ export class AccessPolicyEffects {
|
||||
ofType(AccessPolicyActions.accessPolicyApiBannerError),
|
||||
map((action) => action.response),
|
||||
tap(() => this.dialog.closeAll()),
|
||||
switchMap((response) => of(ErrorActions.addBannerError({ error: response.error })))
|
||||
switchMap((response) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: {
|
||||
errors: [response.error],
|
||||
context: ErrorContextKey.ACCESS_POLICIES
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
@if (policyComponentState$ | async; as policyComponentState) {
|
||||
@if (flowConfiguration$ | async; as flowConfiguration) {
|
||||
<div class="component-access-policies flex flex-col h-full">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.ACCESS_POLICIES"></context-error-banner>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<form [formGroup]="policyForm" class="my-2">
|
||||
|
@ -47,7 +47,7 @@ import { loadTenants, resetTenantsState } from '../../state/tenants/tenants.acti
|
||||
import { loadPolicyComponent, resetPolicyComponentState } from '../../state/policy-component/policy-component.actions';
|
||||
import { selectPolicyComponentState } from '../../state/policy-component/policy-component.selectors';
|
||||
import { PolicyComponentState } from '../../state/policy-component';
|
||||
import { clearBannerErrors } from '../../../../state/error/error.actions';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Component({
|
||||
selector: 'global-access-policies',
|
||||
@ -464,9 +464,10 @@ export class ComponentAccessPolicies implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
this.store.dispatch(resetAccessPolicyState());
|
||||
this.store.dispatch(resetTenantsState());
|
||||
this.store.dispatch(resetPolicyComponentState());
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import { PolicyTable } from '../common/policy-table/policy-table.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ErrorBanner } from '../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ComponentContext, NifiTooltipDirective } from '@nifi/shared';
|
||||
import { ContextErrorBanner } from '../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ComponentAccessPolicies],
|
||||
@ -46,7 +47,8 @@ import { ComponentContext, NifiTooltipDirective } from '@nifi/shared';
|
||||
PolicyTable,
|
||||
MatButtonModule,
|
||||
ErrorBanner,
|
||||
ComponentContext
|
||||
ComponentContext,
|
||||
ContextErrorBanner
|
||||
]
|
||||
})
|
||||
export class ComponentAccessPoliciesModule {}
|
||||
|
@ -23,7 +23,7 @@
|
||||
} @else {
|
||||
@if (flowConfiguration$ | async; as flowConfiguration) {
|
||||
<div class="global-access-policies flex flex-col h-full">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.ACCESS_POLICIES"></context-error-banner>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<form [formGroup]="policyForm" class="my-2">
|
||||
|
@ -21,8 +21,8 @@ import { selectCurrentUser } from '../../../../state/current-user/current-user.s
|
||||
import {
|
||||
createAccessPolicy,
|
||||
openAddTenantToPolicyDialog,
|
||||
promptOverrideAccessPolicy,
|
||||
promptDeleteAccessPolicy,
|
||||
promptOverrideAccessPolicy,
|
||||
promptRemoveTenantFromPolicy,
|
||||
reloadAccessPolicy,
|
||||
resetAccessPolicyState,
|
||||
@ -48,7 +48,7 @@ import { selectFlowConfiguration } from '../../../../state/flow-configuration/fl
|
||||
import { AccessPoliciesState } from '../../state';
|
||||
import { loadTenants, resetTenantsState } from '../../state/tenants/tenants.actions';
|
||||
import { loadCurrentUser } from '../../../../state/current-user/current-user.actions';
|
||||
import { clearBannerErrors } from '../../../../state/error/error.actions';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Component({
|
||||
selector: 'global-access-policies',
|
||||
@ -291,8 +291,9 @@ export class GlobalAccessPolicies implements OnInit, OnDestroy {
|
||||
// reload the current user to ensure the latest global policies
|
||||
this.store.dispatch(loadCurrentUser());
|
||||
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
this.store.dispatch(resetAccessPolicyState());
|
||||
this.store.dispatch(resetTenantsState());
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import { NifiTooltipDirective } from '@nifi/shared';
|
||||
import { PolicyTable } from '../common/policy-table/policy-table.component';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { ErrorBanner } from '../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ContextErrorBanner } from '../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [GlobalAccessPolicies],
|
||||
@ -45,7 +46,8 @@ import { ErrorBanner } from '../../../../ui/common/error-banner/error-banner.com
|
||||
NifiTooltipDirective,
|
||||
PolicyTable,
|
||||
MatButtonModule,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
]
|
||||
})
|
||||
export class GlobalAccessPoliciesModule {}
|
||||
|
@ -22,7 +22,7 @@
|
||||
</header>
|
||||
<div class="pb-5 px-5 flex-1 flex flex-col">
|
||||
<h3 class="primary-color">NiFi Cluster</h3>
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.CLUSTER"></context-error-banner>
|
||||
<div class="flex flex-col h-full gap-y-2">
|
||||
@if (getTabLinks(); as tabs) {
|
||||
<!-- Don't show the tab bar if there is only 1 tab to show -->
|
||||
|
@ -25,10 +25,10 @@ import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { selectClusterListing } from '../state/cluster-listing/cluster-listing.selectors';
|
||||
import { clusterListingFeatureKey } from '../state/cluster-listing';
|
||||
import { ClusterState } from '../state';
|
||||
import { ErrorBanner } from '../../../ui/common/error-banner/error-banner.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { Navigation } from '../../../ui/common/navigation/navigation.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
import { ContextErrorBanner } from '../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('Cluster', () => {
|
||||
let component: Cluster;
|
||||
@ -46,7 +46,7 @@ describe('Cluster', () => {
|
||||
RouterTestingModule,
|
||||
MockComponent(BannerText),
|
||||
MockComponent(Navigation),
|
||||
MockComponent(ErrorBanner)
|
||||
MockComponent(ContextErrorBanner)
|
||||
],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
|
@ -33,7 +33,7 @@ import { CurrentUser } from '../../../state/current-user';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { selectCurrentRoute } from '@nifi/shared';
|
||||
import { resetSystemDiagnostics } from '../../../state/system-diagnostics/system-diagnostics.actions';
|
||||
import { clearBannerErrors } from '../../../state/error/error.actions';
|
||||
import { ErrorContextKey } from '../../../state/error';
|
||||
|
||||
interface TabLink {
|
||||
label: string;
|
||||
@ -83,7 +83,6 @@ export class Cluster implements OnInit, OnDestroy {
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(resetClusterState());
|
||||
this.store.dispatch(resetSystemDiagnostics());
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
}
|
||||
|
||||
refresh() {
|
||||
@ -118,4 +117,6 @@ export class Cluster implements OnInit, OnDestroy {
|
||||
const path = route.routeConfig.path;
|
||||
return this._tabLinks.find((tabLink) => tabLink.link === path);
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { ErrorBanner } from '../../../ui/common/error-banner/error-banner.component';
|
||||
import { BannerText } from '../../../ui/common/banner-text/banner-text.component';
|
||||
import { ContextErrorBanner } from '../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [Cluster],
|
||||
@ -41,7 +42,8 @@ import { BannerText } from '../../../ui/common/banner-text/banner-text.component
|
||||
MatTabsModule,
|
||||
MatIconButton,
|
||||
ErrorBanner,
|
||||
BannerText
|
||||
BannerText,
|
||||
ContextErrorBanner
|
||||
]
|
||||
})
|
||||
export class ClusterModule {}
|
||||
|
@ -64,6 +64,7 @@ import {
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { NiFiCommon, Storage } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ControllerServicesEffects {
|
||||
@ -519,7 +520,6 @@ export class ControllerServicesEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
@ -574,7 +574,13 @@ export class ControllerServicesEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(ControllerServicesActions.controllerServicesBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.CONTROLLER_SERVICES }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -113,6 +113,7 @@ import {
|
||||
} from './index';
|
||||
import { StatusHistoryRequest } from '../../../../state/status-history';
|
||||
import { FetchComponentVersionsRequest } from '../../../../state/shared';
|
||||
import { ErrorContext } from '../../../../state/error';
|
||||
|
||||
const CANVAS_PREFIX = '[Canvas]';
|
||||
|
||||
@ -809,7 +810,10 @@ export const stopVersionControlSuccess = createAction(
|
||||
|
||||
export const flowSnackbarError = createAction(`${CANVAS_PREFIX} Flow Snackbar Error`, props<{ error: string }>());
|
||||
|
||||
export const flowBannerError = createAction(`${CANVAS_PREFIX} Flow Banner Error`, props<{ error: string }>());
|
||||
export const flowBannerError = createAction(
|
||||
`${CANVAS_PREFIX} Flow Banner Error`,
|
||||
props<{ errorContext: ErrorContext }>()
|
||||
);
|
||||
|
||||
export const openShowLocalChangesDialogRequest = createAction(
|
||||
`${CANVAS_PREFIX} Open Show Local Changes Dialog Request`,
|
||||
|
@ -152,11 +152,12 @@ import {
|
||||
} from '../../../../state/property-verification/property-verification.selectors';
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { Storage, NiFiCommon } from '@nifi/shared';
|
||||
import { NiFiCommon, Storage } from '@nifi/shared';
|
||||
import { resetPollingFlowAnalysis } from '../flow-analysis/flow-analysis.actions';
|
||||
import { selectDocumentVisibilityState } from '../../../../state/document-visibility/document-visibility.selectors';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { DocumentVisibility } from '../../../../state/document-visibility';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class FlowEffects {
|
||||
@ -410,7 +411,6 @@ export class FlowEffects {
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(FlowActions.setDragging({ dragging: false }));
|
||||
});
|
||||
})
|
||||
@ -433,7 +433,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.REMOTE_PROCESS_GROUP))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -537,7 +539,6 @@ export class FlowEffects {
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(FlowActions.setDragging({ dragging: false }));
|
||||
});
|
||||
})
|
||||
@ -560,7 +561,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.PROCESS_GROUP))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -581,7 +584,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.PROCESS_GROUP))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -651,7 +656,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.PROCESS_GROUP))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -727,7 +734,6 @@ export class FlowEffects {
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.canvasUtils.removeTempEdge();
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
});
|
||||
})
|
||||
),
|
||||
@ -749,7 +755,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.CONNECTION))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -769,7 +777,6 @@ export class FlowEffects {
|
||||
.afterClosed()
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(FlowActions.setDragging({ dragging: false }));
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
});
|
||||
})
|
||||
),
|
||||
@ -791,7 +798,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.PORT))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -866,7 +875,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.REGISTRY_IMPORT,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -885,7 +897,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.REGISTRY_IMPORT,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -905,7 +920,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.REGISTRY_IMPORT,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -926,7 +944,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.REGISTRY_IMPORT,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -936,7 +957,6 @@ export class FlowEffects {
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(FlowActions.setDragging({ dragging: false }));
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
});
|
||||
} else {
|
||||
this.dialog
|
||||
@ -971,7 +991,9 @@ export class FlowEffects {
|
||||
}
|
||||
})
|
||||
),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.REGISTRY_IMPORT))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -1281,7 +1303,6 @@ export class FlowEffects {
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(
|
||||
FlowActions.selectComponents({
|
||||
request: {
|
||||
@ -1313,7 +1334,6 @@ export class FlowEffects {
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(
|
||||
FlowActions.selectComponents({
|
||||
request: {
|
||||
@ -1531,7 +1551,6 @@ export class FlowEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
if (response != 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
@ -1603,7 +1622,6 @@ export class FlowEffects {
|
||||
});
|
||||
}
|
||||
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(
|
||||
FlowActions.selectComponents({
|
||||
request: {
|
||||
@ -1660,7 +1678,6 @@ export class FlowEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
if (request.entity.id === currentProcessGroupId) {
|
||||
this.store.dispatch(
|
||||
FlowActions.loadProcessGroup({
|
||||
@ -1732,8 +1749,6 @@ export class FlowEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
FlowActions.selectComponents({
|
||||
@ -1802,7 +1817,12 @@ export class FlowEffects {
|
||||
map((action) => action.response),
|
||||
switchMap((response) => {
|
||||
if (response.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(response.errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
response.errorResponse,
|
||||
this.componentTypeToErrorContext(response.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(response.errorResponse));
|
||||
}
|
||||
@ -1810,6 +1830,30 @@ export class FlowEffects {
|
||||
)
|
||||
);
|
||||
|
||||
private componentTypeToErrorContext(type: ComponentType): ErrorContextKey {
|
||||
switch (type) {
|
||||
case ComponentType.Connection:
|
||||
return ErrorContextKey.CONNECTION;
|
||||
case ComponentType.Label:
|
||||
return ErrorContextKey.LABEL;
|
||||
case ComponentType.InputPort:
|
||||
case ComponentType.OutputPort:
|
||||
return ErrorContextKey.PORT;
|
||||
case ComponentType.Processor:
|
||||
return ErrorContextKey.PROCESSOR;
|
||||
case ComponentType.ProcessGroup:
|
||||
return ErrorContextKey.PROCESS_GROUP;
|
||||
case ComponentType.RemoteProcessGroup:
|
||||
return ErrorContextKey.REMOTE_PROCESS_GROUP;
|
||||
case ComponentType.Funnel:
|
||||
return ErrorContextKey.FUNNEL;
|
||||
case ComponentType.ControllerService:
|
||||
return ErrorContextKey.CONTROLLER_SERVICES;
|
||||
default:
|
||||
return ErrorContextKey.FLOW;
|
||||
}
|
||||
}
|
||||
|
||||
updateProcessor$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(FlowActions.updateProcessor),
|
||||
@ -2775,7 +2819,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -2799,7 +2848,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -2901,7 +2955,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -2925,7 +2984,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -3028,7 +3092,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -3055,7 +3124,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -3218,7 +3292,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -3245,7 +3324,12 @@ export class FlowEffects {
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => {
|
||||
if (request.errorStrategy === 'banner') {
|
||||
return of(this.bannerOrFullScreenError(errorResponse));
|
||||
return of(
|
||||
this.bannerOrFullScreenError(
|
||||
errorResponse,
|
||||
this.componentTypeToErrorContext(request.type)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return of(this.snackBarOrFullScreenError(errorResponse));
|
||||
}
|
||||
@ -3403,7 +3487,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.FLOW_VERSION,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -3422,7 +3509,10 @@ export class FlowEffects {
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
context: ErrorContextKey.FLOW_VERSION,
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)]
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -3472,10 +3562,6 @@ export class FlowEffects {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
});
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
@ -3490,7 +3576,9 @@ export class FlowEffects {
|
||||
map((response) => {
|
||||
return FlowActions.saveToFlowRegistrySuccess({ response });
|
||||
}),
|
||||
catchError((errorResponse: HttpErrorResponse) => of(this.bannerOrFullScreenError(errorResponse)))
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(this.bannerOrFullScreenError(errorResponse, ErrorContextKey.FLOW_VERSION))
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
@ -3509,8 +3597,8 @@ export class FlowEffects {
|
||||
flowBannerError$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(FlowActions.flowBannerError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
map((action) => action.errorContext),
|
||||
switchMap((errorContext) => of(ErrorActions.addBannerError({ errorContext })))
|
||||
)
|
||||
);
|
||||
|
||||
@ -3525,10 +3613,13 @@ export class FlowEffects {
|
||||
)
|
||||
);
|
||||
|
||||
private bannerOrFullScreenError(errorResponse: HttpErrorResponse) {
|
||||
private bannerOrFullScreenError(errorResponse: HttpErrorResponse, context: ErrorContextKey) {
|
||||
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
|
||||
return FlowActions.flowBannerError({
|
||||
error: this.errorHelper.getErrorString(errorResponse)
|
||||
errorContext: {
|
||||
errors: [this.errorHelper.getErrorString(errorResponse)],
|
||||
context
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return this.errorHelper.fullScreenError(errorResponse);
|
||||
|
@ -37,6 +37,7 @@ import { selectTimeOffset } from '../../../../state/flow-configuration/flow-conf
|
||||
import { selectAbout } from '../../../../state/about/about.selectors';
|
||||
import { MEDIUM_DIALOG } from 'libs/shared/src';
|
||||
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ManageRemotePortsEffects {
|
||||
@ -126,7 +127,13 @@ export class ManageRemotePortsEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(ManageRemotePortsActions.remotePortsBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.MANAGE_REMOTE_PORTS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -220,8 +227,6 @@ export class ManageRemotePortsEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
ManageRemotePortsActions.selectRemotePort({
|
||||
|
@ -33,7 +33,8 @@ describe('Birdseye', () => {
|
||||
provide: BirdseyeView,
|
||||
useValue: {
|
||||
init: jest.fn(),
|
||||
refresh: jest.fn()
|
||||
refresh: jest.fn(),
|
||||
destroy: jest.fn()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -18,7 +18,7 @@
|
||||
<h2 mat-dialog-title>Create Connection</h2>
|
||||
@if (breadcrumbs$ | async; as breadcrumbs) {
|
||||
<form class="create-connection-form" [formGroup]="createConnectionForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.CONNECTION"></context-error-banner>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Details">
|
||||
<mat-dialog-content>
|
||||
|
@ -57,6 +57,8 @@ import { BreadcrumbEntity } from '../../../../../state/shared';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'create-connection',
|
||||
@ -86,7 +88,8 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
|
||||
DestinationProcessGroup,
|
||||
SourceRemoteProcessGroup,
|
||||
DestinationRemoteProcessGroup,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './create-connection.component.html',
|
||||
styleUrls: ['./create-connection.component.scss']
|
||||
@ -311,4 +314,6 @@ export class CreateConnection extends CloseOnEscapeDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.createConnectionForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
{{ connectionReadonly || sourceReadonly || destinationReadonly ? 'Connection Details' : 'Edit Connection' }}
|
||||
</h2>
|
||||
<form class="edit-connection-form" [formGroup]="editConnectionForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.CONNECTION"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Details">
|
||||
<mat-dialog-content>
|
||||
|
@ -56,6 +56,8 @@ import { DestinationRemoteProcessGroup } from '../destination/destination-remote
|
||||
import { BreadcrumbEntity } from '../../../../../state/shared';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { TabbedDialog } from '../../../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-connection',
|
||||
@ -85,7 +87,8 @@ import { TabbedDialog } from '../../../../../../../ui/common/tabbed-dialog/tabbe
|
||||
DestinationProcessGroup,
|
||||
SourceRemoteProcessGroup,
|
||||
DestinationRemoteProcessGroup,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './edit-connection.component.html',
|
||||
styleUrls: ['./edit-connection.component.scss']
|
||||
@ -438,4 +441,6 @@ export class EditConnectionComponent extends TabbedDialog {
|
||||
override getCancelDialogResult(): any {
|
||||
return 'CANCELLED';
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Import From Registry</h2>
|
||||
<form class="import-from-registry-form" [formGroup]="importFromRegistryForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.REGISTRY_IMPORT"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div class="dialog-content flex flex-col h-full gap-y-2">
|
||||
<div>
|
||||
|
@ -51,6 +51,8 @@ import { Client } from '../../../../../../../service/client.service';
|
||||
import { importFromRegistry } from '../../../../../state/flow/flow.actions';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { isDefinedAndNotNull, SelectOption } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'import-from-registry',
|
||||
@ -74,7 +76,8 @@ import { isDefinedAndNotNull, SelectOption } from 'libs/shared/src';
|
||||
JsonPipe,
|
||||
MatCheckboxModule,
|
||||
MatSortModule,
|
||||
MatTableModule
|
||||
MatTableModule,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './import-from-registry.component.html',
|
||||
styleUrls: ['./import-from-registry.component.scss']
|
||||
@ -414,4 +417,6 @@ export class ImportFromRegistry extends CloseOnEscapeDialog implements OnInit {
|
||||
override isDirty(): boolean {
|
||||
return this.importFromRegistryForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Save Flow Version</h2>
|
||||
<form class="save-version-form" [formGroup]="saveVersionForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.FLOW_VERSION"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
@if (versionControlInformation) {
|
||||
<div class="flex flex-col gap-y-4 mb-6">
|
||||
|
@ -36,6 +36,8 @@ import { SaveVersionDialogRequest, SaveVersionRequest, VersionControlInformation
|
||||
import { TextTip, NiFiCommon, NifiTooltipDirective, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { NgForOf, NgIf } from '@angular/common';
|
||||
import { MatInput } from '@angular/material/input';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'save-version-dialog',
|
||||
@ -57,7 +59,8 @@ import { MatInput } from '@angular/material/input';
|
||||
MatLabel,
|
||||
NgForOf,
|
||||
NgIf,
|
||||
MatInput
|
||||
MatInput,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './save-version-dialog.component.html',
|
||||
styleUrl: './save-version-dialog.component.scss'
|
||||
@ -238,4 +241,6 @@ export class SaveVersionDialog extends CloseOnEscapeDialog implements OnInit {
|
||||
override isDirty(): boolean {
|
||||
return this.saveVersionForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>{{ readonly ? 'Label Details' : 'Edit Label' }}</h2>
|
||||
<form class="edit-label-form" [formGroup]="editLabelForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.LABEL"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
|
@ -35,6 +35,8 @@ import { ClusterConnectionService } from '../../../../../../../service/cluster-c
|
||||
import { MatOption } from '@angular/material/autocomplete';
|
||||
import { MatSelect } from '@angular/material/select';
|
||||
import { NifiTooltipDirective, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-label',
|
||||
@ -51,7 +53,8 @@ import { NifiTooltipDirective, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
NifiSpinnerDirective,
|
||||
MatOption,
|
||||
MatSelect,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-label.component.scss']
|
||||
})
|
||||
@ -115,4 +118,6 @@ export class EditLabel extends CloseOnEscapeDialog {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Create New {{ portTypeLabel }}</h2>
|
||||
<form class="create-port-form" [formGroup]="createPortForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PORT"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ portTypeLabel }} Name</mat-label>
|
||||
|
@ -32,6 +32,8 @@ import { AsyncPipe } from '@angular/common';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nifi-spinner.directive';
|
||||
import { NifiTooltipDirective, TextTip, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'create-port',
|
||||
@ -45,7 +47,8 @@ import { NifiTooltipDirective, TextTip, CloseOnEscapeDialog } from '@nifi/shared
|
||||
MatButtonModule,
|
||||
AsyncPipe,
|
||||
NifiSpinnerDirective,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './create-port.component.html',
|
||||
styleUrls: ['./create-port.component.scss']
|
||||
@ -113,4 +116,5 @@ export class CreatePort extends CloseOnEscapeDialog {
|
||||
}
|
||||
|
||||
protected readonly ComponentType = ComponentType;
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>{{ readonly ? portTypeLabel + ' Details' : 'Edit ' + portTypeLabel }}</h2>
|
||||
<form class="edit-port-form" [formGroup]="editPortForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PORT"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
|
@ -34,6 +34,8 @@ import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nif
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
import { NifiTooltipDirective, TextTip, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
@Component({
|
||||
selector: 'edit-port',
|
||||
standalone: true,
|
||||
@ -47,7 +49,8 @@ import { NifiTooltipDirective, TextTip, CloseOnEscapeDialog } from '@nifi/shared
|
||||
MatButtonModule,
|
||||
AsyncPipe,
|
||||
NifiSpinnerDirective,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-port.component.scss']
|
||||
})
|
||||
@ -127,4 +130,5 @@ export class EditPort extends CloseOnEscapeDialog {
|
||||
}
|
||||
|
||||
protected readonly TextTip = TextTip;
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Create Process Group</h2>
|
||||
<form class="create-process-group-form" [formGroup]="createProcessGroupForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PROCESS_GROUP"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
|
@ -35,6 +35,8 @@ import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators }
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { NiFiCommon, TextTip, NifiTooltipDirective } from '@nifi/shared';
|
||||
import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'create-process-group',
|
||||
@ -51,7 +53,8 @@ import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
NifiTooltipDirective,
|
||||
MatIconModule
|
||||
MatIconModule,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './create-process-group.component.html',
|
||||
styleUrls: ['./create-process-group.component.scss']
|
||||
@ -142,4 +145,6 @@ export class CreateProcessGroup extends CloseOnEscapeDialog {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>{{ readonly ? 'Process Group Details' : 'Edit Process Group' }}</h2>
|
||||
<form class="process-group-edit-form" [formGroup]="editProcessGroupForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PROCESS_GROUP"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -36,6 +36,8 @@ import { EditComponentDialogRequest } from '../../../../../state/flow';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { TabbedDialog } from '../../../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-process-group',
|
||||
@ -55,7 +57,8 @@ import { TabbedDialog } from '../../../../../../../ui/common/tabbed-dialog/tabbe
|
||||
NifiTooltipDirective,
|
||||
FormsModule,
|
||||
ControllerServiceTable,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-process-group.component.scss']
|
||||
})
|
||||
@ -256,4 +259,6 @@ export class EditProcessGroup extends TabbedDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editProcessGroupForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="processor-edit-form" [formGroup]="editProcessorForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PROCESSOR"></context-error-banner>
|
||||
<!-- TODO - Stop & Configure -->
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
|
@ -26,8 +26,8 @@ import { ClusterConnectionService } from '../../../../../../../service/cluster-c
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditProcessor', () => {
|
||||
let component: EditProcessor;
|
||||
@ -727,7 +727,7 @@ describe('EditProcessor', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditProcessor, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditProcessor, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{
|
||||
|
@ -63,6 +63,8 @@ import {
|
||||
} from '../../../../../../../state/property-verification';
|
||||
import { TabbedDialog } from '../../../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { ComponentType, SelectOption } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-processor',
|
||||
@ -84,7 +86,8 @@ import { ComponentType, SelectOption } from 'libs/shared/src';
|
||||
RunDurationSlider,
|
||||
RelationshipSettings,
|
||||
ErrorBanner,
|
||||
PropertyVerification
|
||||
PropertyVerification,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-processor.component.scss']
|
||||
})
|
||||
@ -418,4 +421,6 @@ export class EditProcessor extends TabbedDialog {
|
||||
properties: this.getModifiedProperties()
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Create Remote Process Group</h2>
|
||||
<form class="create-remote-process-group-form" [formGroup]="createRemoteProcessGroupForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.REMOTE_PROCESS_GROUP"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div class="flex gap-x-4">
|
||||
<div class="w-full">
|
||||
|
@ -34,6 +34,8 @@ import { MatIconModule } from '@angular/material/icon';
|
||||
import { CreateComponentRequest } from '../../../../../state/flow';
|
||||
import { NifiTooltipDirective, TextTip } from '@nifi/shared';
|
||||
import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@ -49,7 +51,8 @@ import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
MatOptionModule,
|
||||
MatSelectModule,
|
||||
MatIconModule,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './create-remote-process-group.component.html',
|
||||
styleUrls: ['./create-remote-process-group.component.scss']
|
||||
@ -98,4 +101,5 @@ export class CreateRemoteProcessGroup extends CloseOnEscapeDialog {
|
||||
}
|
||||
|
||||
protected readonly TextTip = TextTip;
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>{{ readonly ? 'Remote Process Group Details' : 'Edit Remote Process Group' }}</h2>
|
||||
<form class="edit-remote-process-group-form" [formGroup]="editRemoteProcessGroupForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.REMOTE_PROCESS_GROUP"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div class="flex flex-col mb-4">
|
||||
<div>Name</div>
|
||||
|
@ -32,6 +32,8 @@ import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-b
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
import { NifiTooltipDirective, TextTip } from '@nifi/shared';
|
||||
import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@ -48,7 +50,8 @@ import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
NifiSpinnerDirective,
|
||||
FormsModule,
|
||||
ErrorBanner,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-remote-process-group.component.scss']
|
||||
})
|
||||
@ -108,4 +111,6 @@ export class EditRemoteProcessGroup extends CloseOnEscapeDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editRemoteProcessGroupForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>Edit Remote {{ portTypeLabel }}</h2>
|
||||
<form class="edit-remote-port-form" [formGroup]="editPortForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.MANAGE_REMOTE_PORTS"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<div class="flex flex-col mb-5">
|
||||
|
@ -35,6 +35,8 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
|
||||
import { NifiTooltipDirective, TextTip } from '@nifi/shared';
|
||||
import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { CanvasState } from '../../../state';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
@ -48,7 +50,8 @@ import { CanvasState } from '../../../state';
|
||||
MatButtonModule,
|
||||
AsyncPipe,
|
||||
NifiSpinnerDirective,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-remote-port.component.scss']
|
||||
})
|
||||
@ -117,4 +120,6 @@ export class EditRemotePortComponent extends CloseOnEscapeDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editPortForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
@if (loading) {
|
||||
<div class="splash h-full p-20">
|
||||
<div class="splash-img h-full flex items-center justify-center">
|
||||
<mat-spinner class="tertiary-spinner "></mat-spinner>
|
||||
<mat-spinner class="tertiary-spinner"></mat-spinner>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
|
@ -60,6 +60,7 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { isDefinedAndNotNull, MEDIUM_DIALOG, SMALL_DIALOG, XL_DIALOG } from 'libs/shared/src';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { NiFiCommon, Storage } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ParameterContextListingEffects {
|
||||
@ -170,10 +171,6 @@ export class ParameterContextListingEffects {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
});
|
||||
})
|
||||
),
|
||||
{ dispatch: false }
|
||||
@ -224,7 +221,13 @@ export class ParameterContextListingEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(ParameterContextListingActions.parameterContextListingBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.PARAMETER_CONTEXTS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -385,8 +388,6 @@ export class ParameterContextListingEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
ParameterContextListingActions.selectParameterContext({
|
||||
|
@ -19,7 +19,7 @@
|
||||
{{ readonly ? 'Parameter Context Details' : isNew ? 'Add Parameter Context' : 'Edit Parameter Context' }}
|
||||
</h2>
|
||||
<form class="parameter-context-edit-form" [formGroup]="editParameterContextForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PARAMETER_CONTEXTS"></context-error-banner>
|
||||
@if ((updateRequest | async)!; as requestEntity) {
|
||||
<mat-dialog-content>
|
||||
<div class="dialog-tab-content flex gap-x-8">
|
||||
|
@ -46,6 +46,8 @@ import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { NiFiCommon, TextTip, NifiTooltipDirective } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-parameter-context',
|
||||
@ -69,7 +71,8 @@ import { NiFiCommon, TextTip, NifiTooltipDirective } from '@nifi/shared';
|
||||
ParameterReferences,
|
||||
RouterLink,
|
||||
ErrorBanner,
|
||||
NifiTooltipDirective
|
||||
NifiTooltipDirective,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-parameter-context.component.scss']
|
||||
})
|
||||
@ -204,4 +207,6 @@ export class EditParameterContext extends TabbedDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editParameterContextForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { isDefinedAndNotNull, NiFiCommon } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class LineageEffects {
|
||||
@ -81,7 +82,11 @@ export class LineageEffects {
|
||||
const query: Lineage = response.lineage;
|
||||
if (query.finished || !this.nifiCommon.isEmpty(query.results.errors)) {
|
||||
response.lineage.results.errors?.forEach((error) => {
|
||||
this.store.dispatch(ErrorActions.addBannerError({ error }));
|
||||
this.store.dispatch(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.LINEAGE }
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return of(LineageActions.deleteLineageQuery());
|
||||
@ -147,7 +152,14 @@ export class LineageEffects {
|
||||
),
|
||||
switchMap((response) => {
|
||||
response.lineage.results.errors?.forEach((error) => {
|
||||
this.store.dispatch(ErrorActions.addBannerError({ error }));
|
||||
this.store.dispatch(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: {
|
||||
errors: [error],
|
||||
context: ErrorContextKey.LINEAGE
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return of(LineageActions.stopPollingLineageQuery());
|
||||
@ -186,7 +198,16 @@ export class LineageEffects {
|
||||
tap(() => {
|
||||
this.store.dispatch(LineageActions.stopPollingLineageQuery());
|
||||
}),
|
||||
switchMap(({ error }) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap(({ error }) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: {
|
||||
errors: [error],
|
||||
context: ErrorContextKey.LINEAGE
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ import { selectClusterSummary } from '../../../../state/cluster-summary/cluster-
|
||||
import { ClusterService } from '../../../../service/cluster.service';
|
||||
import { LARGE_DIALOG, MEDIUM_DIALOG } from 'libs/shared/src';
|
||||
import { Attribute } from '../../../../state/shared';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ProvenanceEventListingEffects {
|
||||
@ -147,7 +148,11 @@ export class ProvenanceEventListingEffects {
|
||||
const query: Provenance = response.provenance;
|
||||
if (query.finished || !this.nifiCommon.isEmpty(query.results.errors)) {
|
||||
response.provenance.results.errors?.forEach((error) => {
|
||||
this.store.dispatch(ErrorActions.addBannerError({ error }));
|
||||
this.store.dispatch(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.REPORTING_TASKS }
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return of(ProvenanceEventListingActions.deleteProvenanceQuery());
|
||||
@ -214,7 +219,11 @@ export class ProvenanceEventListingEffects {
|
||||
),
|
||||
switchMap((response) => {
|
||||
response.provenance.results.errors?.forEach((error) => {
|
||||
this.store.dispatch(ErrorActions.addBannerError({ error }));
|
||||
this.store.dispatch(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.REPORTING_TASKS }
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return of(ProvenanceEventListingActions.stopPollingProvenanceQuery());
|
||||
@ -462,7 +471,13 @@ export class ProvenanceEventListingEffects {
|
||||
tap(() => {
|
||||
this.store.dispatch(ProvenanceEventListingActions.stopPollingProvenanceQuery());
|
||||
}),
|
||||
switchMap(({ error }) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap(({ error }) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.REPORTING_TASKS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -26,8 +26,8 @@ import {
|
||||
ProvenanceResults
|
||||
} from '../../state/provenance-event-listing';
|
||||
import {
|
||||
selectLoadedTimestamp,
|
||||
selectCompletedProvenance,
|
||||
selectLoadedTimestamp,
|
||||
selectProvenanceRequest,
|
||||
selectSearchableFieldsFromRoute,
|
||||
selectStatus
|
||||
@ -50,6 +50,7 @@ import { selectCompletedLineage } from '../../state/lineage/lineage.selectors';
|
||||
import { clearBannerErrors } from '../../../../state/error/error.actions';
|
||||
import { selectClusterSummary } from '../../../../state/cluster-summary/cluster-summary.selectors';
|
||||
import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Component({
|
||||
selector: 'provenance-event-listing',
|
||||
@ -209,7 +210,7 @@ export class ProvenanceEventListing implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
clearBannerErrors(): void {
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
this.store.dispatch(clearBannerErrors({ context: ErrorContextKey.PROVENANCE }));
|
||||
}
|
||||
|
||||
resetLineage(): void {
|
||||
@ -220,6 +221,5 @@ export class ProvenanceEventListing implements OnInit, OnDestroy {
|
||||
this.stateReset = true;
|
||||
this.store.dispatch(resetProvenanceState());
|
||||
this.store.dispatch(resetLineage());
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
-->
|
||||
|
||||
<div class="provenance-event-table h-full flex flex-col">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PROVENANCE"></context-error-banner>
|
||||
<div [class.hidden]="showLineage" class="h-full flex flex-col">
|
||||
<div class="flex flex-col">
|
||||
<div class="flex justify-between mb-1">
|
||||
|
@ -21,7 +21,7 @@ import { ProvenanceEventTable } from './provenance-event-table.component';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('ProvenanceEventTable', () => {
|
||||
let component: ProvenanceEventTable;
|
||||
@ -29,7 +29,7 @@ describe('ProvenanceEventTable', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProvenanceEventTable, MockComponent(ErrorBanner), MatTableModule, NoopAnimationsModule]
|
||||
imports: [ProvenanceEventTable, MockComponent(ContextErrorBanner), MatTableModule, NoopAnimationsModule]
|
||||
});
|
||||
fixture = TestBed.createComponent(ProvenanceEventTable);
|
||||
component = fixture.componentInstance;
|
||||
|
@ -41,6 +41,8 @@ import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.
|
||||
import { ClusterSummary } from '../../../../../state/cluster-summary';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
|
||||
@Component({
|
||||
selector: 'provenance-event-table',
|
||||
@ -64,7 +66,8 @@ import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
MatButtonModule,
|
||||
MatMenu,
|
||||
MatMenuItem,
|
||||
MatMenuTrigger
|
||||
MatMenuTrigger,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./provenance-event-table.component.scss']
|
||||
})
|
||||
@ -402,4 +405,6 @@ export class ProvenanceEventTable implements AfterViewInit {
|
||||
refreshClicked(): void {
|
||||
this.resubmitProvenanceQuery.next();
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
import { stopPollingQueueListingRequest } from './queue-listing.actions';
|
||||
import { LARGE_DIALOG } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class QueueListingEffects {
|
||||
@ -339,7 +340,9 @@ export class QueueListingEffects {
|
||||
tap(() => {
|
||||
this.store.dispatch(QueueListingActions.stopPollingQueueListingRequest());
|
||||
}),
|
||||
switchMap(({ error }) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap(({ error }) =>
|
||||
of(ErrorActions.addBannerError({ errorContext: { errors: [error], context: ErrorContextKey.QUEUE } }))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<div class="flowfile-table h-full flex flex-col">
|
||||
<h3 class="queue-listing-header primary-color">{{ selectedConnection?.label }}</h3>
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.QUEUE"></context-error-banner>
|
||||
<div class="flex justify-between mb-2">
|
||||
<div class="tertiary-color font-medium">
|
||||
Display {{ displayObjectCount }} of {{ formatCount(queueSizeObjectCount) }} ({{
|
||||
|
@ -21,7 +21,7 @@ import { FlowFileTable } from './flowfile-table.component';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('FlowFileTable', () => {
|
||||
let component: FlowFileTable;
|
||||
@ -29,7 +29,7 @@ describe('FlowFileTable', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FlowFileTable, MockComponent(ErrorBanner), MatTableModule, NoopAnimationsModule]
|
||||
imports: [FlowFileTable, MockComponent(ContextErrorBanner), MatTableModule, NoopAnimationsModule]
|
||||
});
|
||||
fixture = TestBed.createComponent(FlowFileTable);
|
||||
component = fixture.componentInstance;
|
||||
|
@ -28,12 +28,23 @@ import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.
|
||||
import { ClusterSummary } from '../../../../../state/cluster-summary';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'flowfile-table',
|
||||
standalone: true,
|
||||
templateUrl: './flowfile-table.component.html',
|
||||
imports: [MatTableModule, RouterLink, ErrorBanner, MatIconButton, MatMenu, MatMenuItem, MatMenuTrigger],
|
||||
imports: [
|
||||
MatTableModule,
|
||||
RouterLink,
|
||||
ErrorBanner,
|
||||
MatIconButton,
|
||||
MatMenu,
|
||||
MatMenuItem,
|
||||
MatMenuTrigger,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./flowfile-table.component.scss']
|
||||
})
|
||||
export class FlowFileTable {
|
||||
@ -152,4 +163,6 @@ export class FlowFileTable {
|
||||
viewContentClicked(summary: FlowFileSummary): void {
|
||||
this.viewContent.next(summary);
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -19,10 +19,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { distinctUntilChanged, filter } from 'rxjs';
|
||||
import {
|
||||
selectConnectionIdFromRoute,
|
||||
selectSelectedConnection,
|
||||
selectCompletedListingRequest,
|
||||
selectConnectionIdFromRoute,
|
||||
selectLoadedTimestamp,
|
||||
selectSelectedConnection,
|
||||
selectStatus
|
||||
} from '../../state/queue-listing/queue-listing.selectors';
|
||||
import { FlowFileSummary } from '../../state/queue-listing';
|
||||
@ -40,7 +40,6 @@ import { NiFiState } from '../../../../state';
|
||||
import { selectAbout } from '../../../../state/about/about.selectors';
|
||||
import { About } from '../../../../state/about';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { clearBannerErrors } from '../../../../state/error/error.actions';
|
||||
import { selectClusterSummary } from '../../../../state/cluster-summary/cluster-summary.selectors';
|
||||
import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions';
|
||||
|
||||
@ -125,6 +124,5 @@ export class QueueListing implements OnInit, OnDestroy {
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(resetQueueListingState());
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ import {
|
||||
} from '../../../../state/property-verification/property-verification.selectors';
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class FlowAnalysisRulesEffects {
|
||||
@ -158,7 +159,13 @@ export class FlowAnalysisRulesEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(FlowAnalysisRuleActions.flowAnalysisRuleBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.FLOW_ANALYSIS_RULES }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -364,7 +371,6 @@ export class FlowAnalysisRulesEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
|
@ -56,6 +56,7 @@ import {
|
||||
} from '../../../../state/property-verification/property-verification.selectors';
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ManagementControllerServicesEffects {
|
||||
@ -391,7 +392,6 @@ export class ManagementControllerServicesEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
@ -445,7 +445,13 @@ export class ManagementControllerServicesEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(ManagementControllerServicesActions.managementControllerServicesBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.CONTROLLER_SERVICES }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -67,6 +67,7 @@ import {
|
||||
} from '../../../../state/property-verification/property-verification.selectors';
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ParameterProvidersEffects {
|
||||
@ -442,7 +443,6 @@ export class ParameterProvidersEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
|
||||
if (response !== 'ROUTED') {
|
||||
@ -613,7 +613,6 @@ export class ParameterProvidersEffects {
|
||||
|
||||
dialogRef.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ParameterProviderActions.resetFetchedParameterProvider());
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
if (response !== 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
@ -640,7 +639,13 @@ export class ParameterProvidersEffects {
|
||||
tap(() =>
|
||||
this.store.dispatch(ParameterProviderActions.stopPollingParameterProviderParametersUpdateRequest())
|
||||
),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.PARAMETER_PROVIDERS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -38,6 +38,7 @@ import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { LARGE_DIALOG, MEDIUM_DIALOG, SMALL_DIALOG } from 'libs/shared/src';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class RegistryClientsEffects {
|
||||
@ -150,7 +151,13 @@ export class RegistryClientsEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(RegistryClientsActions.registryClientsBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.REGISTRY_CLIENTS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -252,8 +259,6 @@ export class RegistryClientsEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
this.store.dispatch(
|
||||
RegistryClientsActions.selectClient({
|
||||
|
@ -51,6 +51,7 @@ import {
|
||||
} from '../../../../state/property-verification/property-verification.selectors';
|
||||
import { VerifyPropertiesRequestContext } from '../../../../state/property-verification';
|
||||
import { BackNavigation } from '../../../../state/navigation';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class ReportingTasksEffects {
|
||||
@ -154,7 +155,13 @@ export class ReportingTasksEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(ReportingTaskActions.reportingTasksBannerApiError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.REPORTING_TASKS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -402,7 +409,6 @@ export class ReportingTasksEffects {
|
||||
});
|
||||
|
||||
editDialogReference.afterClosed().subscribe((response) => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
this.store.dispatch(resetPropertyVerificationState());
|
||||
|
||||
if (response != 'ROUTED') {
|
||||
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="flow-analysis-rule-edit-form" [formGroup]="editFlowAnalysisRuleForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.FLOW_ANALYSIS_RULES"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -25,7 +25,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditFlowAnalysisRule', () => {
|
||||
let component: EditFlowAnalysisRule;
|
||||
@ -96,7 +96,7 @@ describe('EditFlowAnalysisRule', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditFlowAnalysisRule, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditFlowAnalysisRule, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{
|
||||
|
@ -46,6 +46,8 @@ import {
|
||||
import { PropertyVerification } from '../../../../../ui/common/property-verification/property-verification.component';
|
||||
import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { SelectOption } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-flow-analysis-rule',
|
||||
@ -65,7 +67,8 @@ import { SelectOption } from 'libs/shared/src';
|
||||
NifiTooltipDirective,
|
||||
FlowAnalysisRuleTable,
|
||||
ErrorBanner,
|
||||
PropertyVerification
|
||||
PropertyVerification,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-flow-analysis-rule.component.scss']
|
||||
})
|
||||
@ -184,4 +187,6 @@ export class EditFlowAnalysisRule extends TabbedDialog {
|
||||
properties: this.getModifiedProperties()
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="parameter-provider-edit-form" [formGroup]="editParameterProviderForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PARAMETER_PROVIDERS"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -51,6 +51,8 @@ import {
|
||||
} from '../../../../../state/property-verification';
|
||||
import { PropertyVerification } from '../../../../../ui/common/property-verification/property-verification.component';
|
||||
import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-parameter-provider',
|
||||
@ -69,7 +71,8 @@ import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dial
|
||||
ErrorBanner,
|
||||
CommonModule,
|
||||
NifiTooltipDirective,
|
||||
PropertyVerification
|
||||
PropertyVerification,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './edit-parameter-provider.component.html',
|
||||
styleUrls: ['./edit-parameter-provider.component.scss']
|
||||
@ -183,4 +186,6 @@ export class EditParameterProvider extends TabbedDialog {
|
||||
properties: this.getModifiedProperties()
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div>
|
||||
<h2 mat-dialog-title>Fetch Parameters</h2>
|
||||
<form class="parameter-provider-fetch-form" [formGroup]="fetchParametersForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.PARAMETER_PROVIDERS"></context-error-banner>
|
||||
|
||||
<mat-dialog-content *ngIf="(updateRequest | async)! as requestEntity; else fetchFormContent">
|
||||
<div class="dialog-content flex gap-x-4 w-full pt-2">
|
||||
|
@ -47,6 +47,8 @@ import { Store } from '@ngrx/store';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'fetch-parameter-provider-parameters',
|
||||
@ -65,7 +67,8 @@ import { CloseOnEscapeDialog } from '@nifi/shared';
|
||||
MatCheckboxModule,
|
||||
MatInputModule,
|
||||
ParameterReferences,
|
||||
PipesModule
|
||||
PipesModule,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './fetch-parameter-provider-parameters.component.html',
|
||||
styleUrls: ['./fetch-parameter-provider-parameters.component.scss']
|
||||
@ -615,4 +618,6 @@ export class FetchParameterProviderParameters extends CloseOnEscapeDialog implem
|
||||
override isDirty(): boolean {
|
||||
return this.fetchParametersForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="edit-registry-client-form" [formGroup]="editRegistryClientForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.REGISTRY_CLIENTS"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -25,7 +25,7 @@ import { ClusterConnectionService } from '../../../../../service/cluster-connect
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditRegistryClient', () => {
|
||||
let component: EditRegistryClient;
|
||||
@ -109,7 +109,7 @@ describe('EditRegistryClient', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditRegistryClient, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditRegistryClient, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{
|
||||
|
@ -39,6 +39,8 @@ import { PropertyTable } from '../../../../../ui/common/property-table/property-
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-registry-client',
|
||||
@ -56,7 +58,8 @@ import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dial
|
||||
NifiTooltipDirective,
|
||||
MatTabsModule,
|
||||
PropertyTable,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-registry-client.component.scss']
|
||||
})
|
||||
@ -138,4 +141,6 @@ export class EditRegistryClient extends TabbedDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editRegistryClientForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="reporting-task-edit-form" [formGroup]="editReportingTaskForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.REPORTING_TASKS"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -24,8 +24,8 @@ import { EditReportingTaskDialogRequest } from '../../../state/reporting-tasks';
|
||||
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditReportingTask', () => {
|
||||
let component: EditReportingTask;
|
||||
@ -389,7 +389,7 @@ describe('EditReportingTask', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditReportingTask, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditReportingTask, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{
|
||||
|
@ -52,6 +52,8 @@ import {
|
||||
import { PropertyVerification } from '../../../../../ui/common/property-verification/property-verification.component';
|
||||
import { TabbedDialog } from '../../../../../ui/common/tabbed-dialog/tabbed-dialog.component';
|
||||
import { SelectOption } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../../../../ui/common/context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-reporting-task',
|
||||
@ -72,7 +74,8 @@ import { SelectOption } from 'libs/shared/src';
|
||||
NifiSpinnerDirective,
|
||||
NifiTooltipDirective,
|
||||
ErrorBanner,
|
||||
PropertyVerification
|
||||
PropertyVerification,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-reporting-task.component.scss']
|
||||
})
|
||||
@ -246,4 +249,6 @@ export class EditReportingTask extends TabbedDialog {
|
||||
properties: this.getModifiedProperties()
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { LARGE_DIALOG, MEDIUM_DIALOG, SMALL_DIALOG } from 'libs/shared/src';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
|
||||
@Injectable()
|
||||
export class UserListingEffects {
|
||||
@ -233,7 +234,9 @@ export class UserListingEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(UserListingActions.usersApiBannerError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(ErrorActions.addBannerError({ errorContext: { errors: [error], context: ErrorContextKey.USERS } }))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@ -393,8 +396,6 @@ export class UserListingEffects {
|
||||
});
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
this.store.dispatch(
|
||||
selectTenant({
|
||||
id: request.user.id
|
||||
@ -601,8 +602,6 @@ export class UserListingEffects {
|
||||
});
|
||||
|
||||
dialogReference.afterClosed().subscribe(() => {
|
||||
this.store.dispatch(ErrorActions.clearBannerErrors());
|
||||
|
||||
this.store.dispatch(
|
||||
selectTenant({
|
||||
id: request.userGroup.id
|
||||
|
@ -31,6 +31,7 @@ import { isDefinedAndNotNull, LARGE_DIALOG } from 'libs/shared/src';
|
||||
import * as ErrorActions from '../error/error.actions';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHelper } from '../../service/error-helper.service';
|
||||
import { ErrorContextKey } from '../error';
|
||||
|
||||
@Injectable()
|
||||
export class ComponentStateEffects {
|
||||
@ -109,10 +110,15 @@ export class ComponentStateEffects {
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
error: this.errorHelper.getErrorString(
|
||||
errorResponse,
|
||||
'Failed to clear the component state.'
|
||||
)
|
||||
errorContext: {
|
||||
errors: [
|
||||
this.errorHelper.getErrorString(
|
||||
errorResponse,
|
||||
'Failed to clear the component state.'
|
||||
)
|
||||
],
|
||||
context: ErrorContextKey.COMPONENT_STATE
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
@ -139,10 +145,15 @@ export class ComponentStateEffects {
|
||||
catchError((errorResponse: HttpErrorResponse) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
error: this.errorHelper.getErrorString(
|
||||
errorResponse,
|
||||
'Failed to reload the component state.'
|
||||
)
|
||||
errorContext: {
|
||||
errors: [
|
||||
this.errorHelper.getErrorString(
|
||||
errorResponse,
|
||||
'Failed to reload the component state.'
|
||||
)
|
||||
],
|
||||
context: ErrorContextKey.COMPONENT_STATE
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { createAction, props } from '@ngrx/store';
|
||||
import { ErrorDetail } from './index';
|
||||
import { ErrorContext, ErrorContextKey, ErrorDetail } from './index';
|
||||
|
||||
export const fullScreenError = createAction(
|
||||
'[Error] Full Screen Error',
|
||||
@ -25,9 +25,9 @@ export const fullScreenError = createAction(
|
||||
|
||||
export const snackBarError = createAction('[Error] Snackbar Error', props<{ error: string }>());
|
||||
|
||||
export const addBannerError = createAction('[Error] Add Banner Error', props<{ error: string }>());
|
||||
export const addBannerError = createAction('[Error] Add Banner Error', props<{ errorContext: ErrorContext }>());
|
||||
|
||||
export const clearBannerErrors = createAction('[Error] Clear Banner Errors');
|
||||
export const clearBannerErrors = createAction('[Error] Clear Banner Errors', props<{ context: ErrorContextKey }>());
|
||||
|
||||
export const resetErrorState = createAction('[Error] Reset Error State');
|
||||
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
import { produce } from 'immer';
|
||||
|
||||
export const initialState: ErrorState = {
|
||||
bannerErrors: null,
|
||||
bannerErrors: {},
|
||||
fullScreenError: null,
|
||||
routedToFullScreenError: false
|
||||
};
|
||||
@ -38,19 +38,25 @@ export const errorReducer = createReducer(
|
||||
...state,
|
||||
fullScreenError: errorDetail
|
||||
})),
|
||||
on(addBannerError, (state, { error }) => {
|
||||
on(addBannerError, (state, { errorContext }) => {
|
||||
return produce(state, (draftState) => {
|
||||
if (draftState.bannerErrors === null) {
|
||||
draftState.bannerErrors = [];
|
||||
draftState.bannerErrors = {};
|
||||
}
|
||||
if (!draftState.bannerErrors[errorContext.context]) {
|
||||
draftState.bannerErrors[errorContext.context] = [];
|
||||
}
|
||||
|
||||
draftState.bannerErrors.push(error);
|
||||
draftState.bannerErrors[errorContext.context].push(...errorContext.errors);
|
||||
});
|
||||
}),
|
||||
on(clearBannerErrors, (state, { context }) => {
|
||||
return produce(state, (draftState) => {
|
||||
if (draftState.bannerErrors && draftState.bannerErrors[context]) {
|
||||
delete draftState.bannerErrors[context];
|
||||
}
|
||||
});
|
||||
}),
|
||||
on(clearBannerErrors, (state) => ({
|
||||
...state,
|
||||
bannerErrors: null
|
||||
})),
|
||||
on(setRoutedToFullScreenError, (state, { routedToFullScreenError }) => ({
|
||||
...state,
|
||||
routedToFullScreenError
|
||||
|
@ -16,13 +16,16 @@
|
||||
*/
|
||||
|
||||
import { createFeatureSelector, createSelector } from '@ngrx/store';
|
||||
import { errorFeatureKey, ErrorState } from './index';
|
||||
import { BannerErrors, errorFeatureKey, ErrorState } from './index';
|
||||
|
||||
export const selectErrorState = createFeatureSelector<ErrorState>(errorFeatureKey);
|
||||
|
||||
export const selectFullScreenError = createSelector(selectErrorState, (state: ErrorState) => state.fullScreenError);
|
||||
|
||||
export const selectBannerErrors = createSelector(selectErrorState, (state: ErrorState) => state.bannerErrors);
|
||||
export const selectAllBannerErrors = createSelector(selectErrorState, (state: ErrorState) => state.bannerErrors);
|
||||
|
||||
export const selectBannerErrors = (context: string) =>
|
||||
createSelector(selectAllBannerErrors, (bannerErrors: BannerErrors) => bannerErrors[context] || []);
|
||||
|
||||
export const selectRoutedToFullScreenError = createSelector(
|
||||
selectErrorState,
|
||||
|
@ -22,8 +22,48 @@ export interface ErrorDetail {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export enum ErrorContextKey {
|
||||
ACCESS_POLICIES = 'access-policies',
|
||||
QUEUE = 'queue',
|
||||
CLUSTER = 'cluster',
|
||||
PROVENANCE = 'provenance',
|
||||
COMPONENT_STATE = 'component-state',
|
||||
STATUS_HISTORY = 'status-history',
|
||||
SYSTEM_DIAGNOSTICS = 'system-diagnostics',
|
||||
CONTROLLER_SERVICES = 'controller-services',
|
||||
FLOW = 'flow',
|
||||
MANAGE_REMOTE_PORTS = 'manage-remote-ports',
|
||||
PARAMETER_CONTEXTS = 'parameter-contexts',
|
||||
PARAMETER_PROVIDERS = 'parameter-providers',
|
||||
REGISTRY_CLIENTS = 'registry-clients',
|
||||
REPORTING_TASKS = 'report-tasks',
|
||||
USERS = 'users',
|
||||
PROCESS_GROUP = 'process-group',
|
||||
REMOTE_PROCESS_GROUP = 'remote-process-group',
|
||||
PROCESSOR = 'processor',
|
||||
CONNECTION = 'connection',
|
||||
PORT = 'port',
|
||||
REGISTRY_IMPORT = 'registry-import',
|
||||
LABEL = 'label',
|
||||
FLOW_VERSION = 'flow-version',
|
||||
FUNNEL = 'funnel',
|
||||
LOCAL_EXTENSIONS = 'local-extensions',
|
||||
LINEAGE = 'lineage',
|
||||
FLOW_ANALYSIS_RULES = 'flow-analysis-rules'
|
||||
}
|
||||
|
||||
export interface ErrorContext {
|
||||
context: ErrorContextKey;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export interface BannerErrors {
|
||||
// key should be the ErrorContextKey of the banner error
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface ErrorState {
|
||||
bannerErrors: string[] | null;
|
||||
bannerErrors: BannerErrors;
|
||||
fullScreenError: ErrorDetail | null;
|
||||
routedToFullScreenError: boolean;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import { StatusHistory } from '../../ui/common/status-history/status-history.com
|
||||
import * as ErrorActions from '../../state/error/error.actions';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHelper } from '../../service/error-helper.service';
|
||||
import { ErrorContextKey } from '../error';
|
||||
|
||||
@Injectable()
|
||||
export class StatusHistoryEffects {
|
||||
@ -200,7 +201,13 @@ export class StatusHistoryEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(StatusHistoryActions.statusHistoryBannerError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.STATUS_HISTORY }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -29,6 +29,7 @@ import { LARGE_DIALOG } from 'libs/shared/src';
|
||||
import * as ErrorActions from '../error/error.actions';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHelper } from '../../service/error-helper.service';
|
||||
import { ErrorContextKey } from '../error';
|
||||
|
||||
@Injectable()
|
||||
export class SystemDiagnosticsEffects {
|
||||
@ -125,7 +126,13 @@ export class SystemDiagnosticsEffects {
|
||||
this.actions$.pipe(
|
||||
ofType(SystemDiagnosticsActions.systemDiagnosticsBannerError),
|
||||
map((action) => action.error),
|
||||
switchMap((error) => of(ErrorActions.addBannerError({ error })))
|
||||
switchMap((error) =>
|
||||
of(
|
||||
ErrorActions.addBannerError({
|
||||
errorContext: { errors: [error], context: ErrorContextKey.SYSTEM_DIAGNOSTICS }
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<div class="component-state-dialog">
|
||||
<h2 mat-dialog-title>Component State</h2>
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.COMPONENT_STATE"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div class="dialog-content flex flex-col justify-between gap-y-5">
|
||||
@if (componentName$ | async; as componentName) {
|
||||
|
@ -15,13 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AfterViewInit, Component, DestroyRef, inject, Input, OnDestroy } from '@angular/core';
|
||||
import { AfterViewInit, Component, DestroyRef, inject, Input } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule, Sort } from '@angular/material/sort';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { NifiTooltipDirective, NiFiCommon, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { CloseOnEscapeDialog, NiFiCommon, NifiTooltipDirective } from '@nifi/shared';
|
||||
import { NifiSpinnerDirective } from '../spinner/nifi-spinner.directive';
|
||||
import { ComponentStateState, StateEntry, StateItem, StateMap } from '../../../state/component-state';
|
||||
import { Store } from '@ngrx/store';
|
||||
@ -39,7 +39,8 @@ import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { selectClusterSummary } from '../../../state/cluster-summary/cluster-summary.selectors';
|
||||
import { ErrorBanner } from '../error-banner/error-banner.component';
|
||||
import { clearBannerErrors } from '../../../state/error/error.actions';
|
||||
import { ErrorContextKey } from '../../../state/error';
|
||||
import { ContextErrorBanner } from '../context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'component-state',
|
||||
@ -56,11 +57,12 @@ import { clearBannerErrors } from '../../../state/error/error.actions';
|
||||
ReactiveFormsModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./component-state.component.scss']
|
||||
})
|
||||
export class ComponentStateDialog extends CloseOnEscapeDialog implements AfterViewInit, OnDestroy {
|
||||
export class ComponentStateDialog extends CloseOnEscapeDialog implements AfterViewInit {
|
||||
@Input() initialSortColumn: 'key' | 'value' = 'key';
|
||||
@Input() initialSortDirection: 'asc' | 'desc' = 'asc';
|
||||
|
||||
@ -143,10 +145,6 @@ export class ComponentStateDialog extends CloseOnEscapeDialog implements AfterVi
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
}
|
||||
|
||||
processStateMap(stateMap: StateMap, clusterState: boolean): StateItem[] {
|
||||
const stateEntries: StateEntry[] = stateMap.state ? stateMap.state : [];
|
||||
|
||||
@ -202,4 +200,6 @@ export class ComponentStateDialog extends CloseOnEscapeDialog implements AfterVi
|
||||
clearState(): void {
|
||||
this.store.dispatch(clearComponentState());
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<error-banner [messages]="messages$ | async" (dismiss)="dismiss()"></error-banner>
|
@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* 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.
|
||||
*/
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ContextErrorBanner } from './context-error-banner.component';
|
||||
import { ErrorContextKey, errorFeatureKey } from '../../../state/error';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState as initialErrorState } from '../../../state/error/error.reducer';
|
||||
|
||||
describe('ContextErrorBanner', () => {
|
||||
let component: ContextErrorBanner;
|
||||
let fixture: ComponentFixture<ContextErrorBanner>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ContextErrorBanner],
|
||||
providers: [
|
||||
provideMockStore({
|
||||
initialState: {
|
||||
[errorFeatureKey]: initialErrorState
|
||||
}
|
||||
})
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContextErrorBanner);
|
||||
component = fixture.componentInstance;
|
||||
component.context = ErrorContextKey.ACCESS_POLICIES;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 { Component, DestroyRef, inject, Input, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NiFiState } from '../../../state';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { clearBannerErrors } from '../../../state/error/error.actions';
|
||||
import { Observable } from 'rxjs';
|
||||
import { selectBannerErrors } from '../../../state/error/error.selectors';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { ErrorBanner } from '../error-banner/error-banner.component';
|
||||
import { ErrorContextKey } from '../../../state/error';
|
||||
|
||||
@Component({
|
||||
selector: 'context-error-banner',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ErrorBanner],
|
||||
templateUrl: './context-error-banner.component.html',
|
||||
styleUrl: './context-error-banner.component.scss'
|
||||
})
|
||||
export class ContextErrorBanner implements OnDestroy {
|
||||
private _context!: ErrorContextKey;
|
||||
@Input({ required: true }) set context(context: ErrorContextKey) {
|
||||
this._context = context;
|
||||
this.messages$ = this.store.select(selectBannerErrors(this._context)).pipe(takeUntilDestroyed(this.destroyRef));
|
||||
}
|
||||
|
||||
get context() {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
messages$: Observable<string[]> | null = null;
|
||||
private destroyRef: DestroyRef = inject(DestroyRef);
|
||||
|
||||
constructor(private store: Store<NiFiState>) {}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.store.dispatch(clearBannerErrors({ context: this.context }));
|
||||
}
|
||||
|
||||
dismiss() {
|
||||
this.store.dispatch(clearBannerErrors({ context: this.context }));
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@
|
||||
</div>
|
||||
</h2>
|
||||
<form class="controller-service-edit-form" [formGroup]="editControllerServiceForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.CONTROLLER_SERVICES"></context-error-banner>
|
||||
<mat-tab-group [(selectedIndex)]="selectedIndex" (selectedIndexChange)="tabChanged($event)">
|
||||
<mat-tab label="Settings">
|
||||
<mat-dialog-content>
|
||||
|
@ -24,8 +24,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
|
||||
|
||||
import 'codemirror/addon/hint/show-hint';
|
||||
import { ErrorBanner } from '../../error-banner/error-banner.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ContextErrorBanner } from '../../context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditControllerService', () => {
|
||||
let component: EditControllerService;
|
||||
@ -548,7 +548,7 @@ describe('EditControllerService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditControllerService, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditControllerService, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{
|
||||
|
@ -52,6 +52,8 @@ import {
|
||||
VerifyPropertiesRequestContext
|
||||
} from '../../../../state/property-verification';
|
||||
import { TabbedDialog } from '../../tabbed-dialog/tabbed-dialog.component';
|
||||
import { ErrorContextKey } from '../../../../state/error';
|
||||
import { ContextErrorBanner } from '../../context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-controller-service',
|
||||
@ -73,7 +75,8 @@ import { TabbedDialog } from '../../tabbed-dialog/tabbed-dialog.component';
|
||||
NifiSpinnerDirective,
|
||||
ErrorBanner,
|
||||
NifiTooltipDirective,
|
||||
PropertyVerification
|
||||
PropertyVerification,
|
||||
ContextErrorBanner
|
||||
],
|
||||
styleUrls: ['./edit-controller-service.component.scss']
|
||||
})
|
||||
@ -214,4 +217,6 @@ export class EditControllerService extends TabbedDialog {
|
||||
properties: this.getModifiedProperties()
|
||||
});
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
<h2 mat-dialog-title>{{ isNew ? 'Add' : 'Edit' }} {{ isUser ? 'User' : 'User Group' }}</h2>
|
||||
<form class="edit-tenant-form" [formGroup]="editTenantForm">
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.USERS"></context-error-banner>
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<mat-radio-group formControlName="tenantType" (change)="tenantTypeChanged()">
|
||||
|
@ -21,8 +21,8 @@ import { EditTenantDialog } from './edit-tenant-dialog.component';
|
||||
import { EditTenantRequest } from '../../../state/shared';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ErrorBanner } from '../error-banner/error-banner.component';
|
||||
import { MockComponent } from 'ng-mocks';
|
||||
import { ContextErrorBanner } from '../context-error-banner/context-error-banner.component';
|
||||
|
||||
describe('EditTenantDialog', () => {
|
||||
let component: EditTenantDialog;
|
||||
@ -786,7 +786,7 @@ describe('EditTenantDialog', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [EditTenantDialog, MockComponent(ErrorBanner), NoopAnimationsModule],
|
||||
imports: [EditTenantDialog, MockComponent(ContextErrorBanner), NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MAT_DIALOG_DATA, useValue: data },
|
||||
{ provide: MatDialogRef, useValue: null }
|
||||
|
@ -41,6 +41,8 @@ import { MatListModule } from '@angular/material/list';
|
||||
import { Client } from '../../../service/client.service';
|
||||
import { NiFiCommon, CloseOnEscapeDialog } from '@nifi/shared';
|
||||
import { ErrorBanner } from '../error-banner/error-banner.component';
|
||||
import { ErrorContextKey } from '../../../state/error';
|
||||
import { ContextErrorBanner } from '../context-error-banner/context-error-banner.component';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-tenant-dialog',
|
||||
@ -57,7 +59,8 @@ import { ErrorBanner } from '../error-banner/error-banner.component';
|
||||
NifiSpinnerDirective,
|
||||
AsyncPipe,
|
||||
MatListModule,
|
||||
ErrorBanner
|
||||
ErrorBanner,
|
||||
ContextErrorBanner
|
||||
],
|
||||
templateUrl: './edit-tenant-dialog.component.html',
|
||||
styleUrls: ['./edit-tenant-dialog.component.scss']
|
||||
@ -258,4 +261,6 @@ export class EditTenantDialog extends CloseOnEscapeDialog {
|
||||
override isDirty(): boolean {
|
||||
return this.editTenantForm.dirty;
|
||||
}
|
||||
|
||||
protected readonly ErrorContextKey = ErrorContextKey;
|
||||
}
|
||||
|
@ -15,9 +15,16 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
@if ((messages$ | async)!; as messages) {
|
||||
<div class="mb-4">
|
||||
<div class="banner-container border-t border-b px-6 py-3 flex justify-between items-center">
|
||||
@if (messages && messages.length > 0) {
|
||||
<div
|
||||
class="banner-container px-4 py-3 mb-5 flex flex-col justify-between text-sm"
|
||||
[ngClass]="{ 'border-t': showBorder, 'border-b': showBorder }">
|
||||
<div
|
||||
class="flex gap-4"
|
||||
[ngClass]="{ 'items-start': messages.length > 1, 'items-center': messages.length === 1 }">
|
||||
@if (showErrorIcon) {
|
||||
<i class="fa fa-exclamation-triangle error-color fa-2x"></i>
|
||||
}
|
||||
@if (messages.length === 1) {
|
||||
<div>{{ messages[0] }}</div>
|
||||
} @else {
|
||||
@ -27,9 +34,9 @@
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
<div class="flex flex-col mt-auto">
|
||||
<button mat-stroked-button (click)="dismiss()">Dismiss</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-1 mt-auto justify-end w-full">
|
||||
<button mat-flat-button class="error-button" (click)="dismissClicked()">Dismiss</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ErrorBanner } from './error-banner.component';
|
||||
import { provideMockStore } from '@ngrx/store/testing';
|
||||
import { initialState } from '../../../state/error/error.reducer';
|
||||
|
||||
describe('ErrorBanner', () => {
|
||||
let component: ErrorBanner;
|
||||
@ -27,8 +25,7 @@ describe('ErrorBanner', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ErrorBanner],
|
||||
providers: [provideMockStore({ initialState })]
|
||||
imports: [ErrorBanner]
|
||||
});
|
||||
fixture = TestBed.createComponent(ErrorBanner);
|
||||
component = fixture.componentInstance;
|
||||
|
@ -15,27 +15,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { NgClass } 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: 'error-banner',
|
||||
standalone: true,
|
||||
imports: [MatButtonModule, AsyncPipe],
|
||||
imports: [MatButtonModule, NgClass],
|
||||
templateUrl: './error-banner.component.html',
|
||||
styleUrls: ['./error-banner.component.scss']
|
||||
})
|
||||
export class ErrorBanner {
|
||||
messages$ = this.store.select(selectBannerErrors);
|
||||
@Input() messages: string[] | null = null;
|
||||
@Input() showErrorIcon = true;
|
||||
@Input() showBorder = true;
|
||||
|
||||
constructor(private store: Store<NiFiState>) {}
|
||||
@Output() dismiss: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
dismiss(): void {
|
||||
this.store.dispatch(clearBannerErrors());
|
||||
dismissClicked(): void {
|
||||
this.dismiss.next();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<error-banner></error-banner>
|
||||
<context-error-banner [context]="ErrorContextKey.STATUS_HISTORY"></context-error-banner>
|
||||
<div class="status-history flex flex-col grow">
|
||||
<mat-dialog-content class="grow flex flex-1">
|
||||
<form [formGroup]="statusHistoryForm" class="flex flex-1 h-full">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user