[NIFI-12519] - deselect parameter when it is deleted. (#8166)

* [NIFI-12519] - deselect parameter when it is deleted. also, enable goto controller service linking

* Added confirmation dialog in controller service edit when the form is dirty and the user attempts to route away using a referencing component link

* Reset state when closing the summary and counter pages.

This closes #8166
This commit is contained in:
Rob Fellows 2023-12-19 10:16:50 -05:00 committed by GitHub
parent 6b7b7cccf8
commit 703948b1d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 104 additions and 41 deletions

View File

@ -19,6 +19,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state'; import { NiFiState } from '../../../state';
import { startUserPolling, stopUserPolling } from '../../../state/user/user.actions'; import { startUserPolling, stopUserPolling } from '../../../state/user/user.actions';
import { resetCounterState } from '../state/counter-listing/counter-listing.actions';
@Component({ @Component({
selector: 'counters', selector: 'counters',
@ -33,6 +34,7 @@ export class Counters implements OnInit, OnDestroy {
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.store.dispatch(resetCounterState());
this.store.dispatch(stopUserPolling()); this.store.dispatch(stopUserPolling());
} }
} }

View File

@ -43,3 +43,5 @@ export const resetCounterSuccess = createAction(
`${COUNTER_PREFIX} Reset Counter Success`, `${COUNTER_PREFIX} Reset Counter Success`,
props<{ response: ResetCounterSuccess }>() props<{ response: ResetCounterSuccess }>()
); );
export const resetCounterState = createAction(`${COUNTER_PREFIX} Reset Counter State`);

View File

@ -17,7 +17,7 @@
import { CounterListingState } from './index'; import { CounterListingState } from './index';
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { loadCounters, loadCountersSuccess, resetCounterSuccess } from './counter-listing.actions'; import { loadCounters, loadCountersSuccess, resetCounterState, resetCounterSuccess } from './counter-listing.actions';
import { parameterContextListingApiError } from '../../../parameter-contexts/state/parameter-context-listing/parameter-context-listing.actions'; import { parameterContextListingApiError } from '../../../parameter-contexts/state/parameter-context-listing/parameter-context-listing.actions';
import { produce } from 'immer'; import { produce } from 'immer';
@ -57,5 +57,8 @@ export const counterListingReducer = createReducer(
}; };
} }
}); });
}) }),
on(resetCounterState, (state) => ({
...initialState
}))
); );

View File

@ -42,6 +42,8 @@ import { Client } from '../../../../service/client.service';
import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
import { EditControllerService } from '../../../../ui/common/controller-service/edit-controller-service/edit-controller-service.component'; import { EditControllerService } from '../../../../ui/common/controller-service/edit-controller-service/edit-controller-service.component';
import { import {
ComponentType,
ControllerServiceReferencingComponent,
EditParameterRequest, EditParameterRequest,
EditParameterResponse, EditParameterResponse,
InlineServiceCreationRequest, InlineServiceCreationRequest,
@ -277,6 +279,13 @@ export class ControllerServicesEffects {
} }
}; };
editDialogReference.componentInstance.goToReferencingComponent = (
component: ControllerServiceReferencingComponent
) => {
const route: string[] = this.getRouteForReference(component);
goTo(route, component.referenceType);
};
if (parameterContext != null) { if (parameterContext != null) {
editDialogReference.componentInstance.getParameters = (sensitive: boolean) => { editDialogReference.componentInstance.getParameters = (sensitive: boolean) => {
return this.flowService.getParameterContext(parameterContext.id).pipe( return this.flowService.getParameterContext(parameterContext.id).pipe(
@ -606,4 +615,24 @@ export class ControllerServicesEffects {
), ),
{ dispatch: false } { dispatch: false }
); );
private getRouteForReference(reference: ControllerServiceReferencingComponent): string[] {
if (reference.referenceType == 'ControllerService') {
if (reference.groupId == null) {
return ['/settings', 'management-controller-services', reference.id];
} else {
return ['/process-groups', reference.groupId, 'controller-services', reference.id];
}
} else if (reference.referenceType == 'ReportingTask') {
return ['/settings', 'reporting-tasks', reference.id];
} else if (reference.referenceType == 'Processor') {
return ['/process-groups', reference.groupId, ComponentType.Processor, reference.id];
} else if (reference.referenceType == 'FlowAnalysisRule') {
return ['/settings', 'flow-analysis-rules', reference.id];
} else if (reference.referenceType == 'ParameterProvider') {
return ['/settings', 'parameter-providers', reference.id];
} else {
return ['/settings', 'registry-clients', reference.id];
}
}
} }

View File

@ -67,6 +67,7 @@ import { CreatePort } from '../../ui/canvas/items/port/create-port/create-port.c
import { EditPort } from '../../ui/canvas/items/port/edit-port/edit-port.component'; import { EditPort } from '../../ui/canvas/items/port/edit-port/edit-port.component';
import { import {
ComponentType, ComponentType,
ControllerServiceReferencingComponent,
EditParameterRequest, EditParameterRequest,
EditParameterResponse, EditParameterResponse,
InlineServiceCreationRequest, InlineServiceCreationRequest,

View File

@ -129,7 +129,7 @@ export class ParameterReferences {
} }
getRouteForReference(reference: AffectedComponent): string[] { getRouteForReference(reference: AffectedComponent): string[] {
if (reference.referenceType == 'ControllerService') { if (reference.referenceType === 'CONTROLLER_SERVICE') {
if (reference.processGroupId == null) { if (reference.processGroupId == null) {
return ['/settings', 'management-controller-services', reference.id]; return ['/settings', 'management-controller-services', reference.id];
} else { } else {

View File

@ -74,7 +74,7 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor {
displayedColumns: string[] = ['name', 'value', 'actions']; displayedColumns: string[] = ['name', 'value', 'actions'];
dataSource: MatTableDataSource<ParameterItem> = new MatTableDataSource<ParameterItem>(); dataSource: MatTableDataSource<ParameterItem> = new MatTableDataSource<ParameterItem>();
selectedItem!: ParameterItem; selectedItem: ParameterItem | null = null;
isDisabled: boolean = false; isDisabled: boolean = false;
isTouched: boolean = false; isTouched: boolean = false;
@ -280,7 +280,7 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor {
item.entity.parameter.valueRemoved = true; item.entity.parameter.valueRemoved = true;
item.deleted = true; item.deleted = true;
item.dirty = true; item.dirty = true;
this.selectParameter(null);
this.handleChanged(); this.handleChanged();
} }
} }
@ -327,7 +327,7 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor {
}); });
} }
selectParameter(item: ParameterItem): void { selectParameter(item: ParameterItem | null): void {
this.selectedItem = item; this.selectedItem = item;
} }

View File

@ -29,6 +29,8 @@ import { Client } from '../../../../service/client.service';
import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
import { EditControllerService } from '../../../../ui/common/controller-service/edit-controller-service/edit-controller-service.component'; import { EditControllerService } from '../../../../ui/common/controller-service/edit-controller-service/edit-controller-service.component';
import { import {
ComponentType,
ControllerServiceReferencingComponent,
InlineServiceCreationRequest, InlineServiceCreationRequest,
InlineServiceCreationResponse, InlineServiceCreationResponse,
NewPropertyDialogRequest, NewPropertyDialogRequest,
@ -220,12 +222,12 @@ export class ManagementControllerServicesEffects {
); );
}; };
const goTo = (commands: string[]): void => { const goTo = (commands: string[], destination: string): void => {
if (editDialogReference.componentInstance.editControllerServiceForm.dirty) { if (editDialogReference.componentInstance.editControllerServiceForm.dirty) {
const saveChangesDialogReference = this.dialog.open(YesNoDialog, { const saveChangesDialogReference = this.dialog.open(YesNoDialog, {
data: { data: {
title: 'Controller Service Configuration', title: 'Controller Service Configuration',
message: `Save changes before going to this Controller Service?` message: `Save changes before going to this ${destination}?`
}, },
panelClass: 'small-dialog' panelClass: 'small-dialog'
}); });
@ -246,7 +248,14 @@ export class ManagementControllerServicesEffects {
editDialogReference.componentInstance.goToService = (serviceId: string) => { editDialogReference.componentInstance.goToService = (serviceId: string) => {
const commands: string[] = ['/settings', 'management-controller-services', serviceId]; const commands: string[] = ['/settings', 'management-controller-services', serviceId];
goTo(commands); goTo(commands, 'Controller Service');
};
editDialogReference.componentInstance.goToReferencingComponent = (
component: ControllerServiceReferencingComponent
) => {
const route: string[] = this.getRouteForReference(component);
goTo(route, component.referenceType);
}; };
editDialogReference.componentInstance.createNewService = ( editDialogReference.componentInstance.createNewService = (
@ -465,4 +474,24 @@ export class ManagementControllerServicesEffects {
), ),
{ dispatch: false } { dispatch: false }
); );
private getRouteForReference(reference: ControllerServiceReferencingComponent): string[] {
if (reference.referenceType == 'ControllerService') {
if (reference.groupId == null) {
return ['/settings', 'management-controller-services', reference.id];
} else {
return ['/process-groups', reference.groupId, 'controller-services', reference.id];
}
} else if (reference.referenceType == 'ReportingTask') {
return ['/settings', 'reporting-tasks', reference.id];
} else if (reference.referenceType == 'Processor') {
return ['/process-groups', reference.groupId, ComponentType.Processor, reference.id];
} else if (reference.referenceType == 'FlowAnalysisRule') {
return ['/settings', 'flow-analysis-rules', reference.id];
} else if (reference.referenceType == 'ParameterProvider') {
return ['/settings', 'parameter-providers', reference.id];
} else {
return ['/settings', 'registry-clients', reference.id];
}
}
} }

View File

@ -19,7 +19,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state'; import { NiFiState } from '../../../state';
import { startUserPolling, stopUserPolling } from '../../../state/user/user.actions'; import { startUserPolling, stopUserPolling } from '../../../state/user/user.actions';
import { loadSummaryListing } from '../state/summary-listing/summary-listing.actions'; import { loadSummaryListing, resetSummaryState } from '../state/summary-listing/summary-listing.actions';
interface TabLink { interface TabLink {
label: string; label: string;
@ -49,6 +49,7 @@ export class Summary implements OnInit, OnDestroy {
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.store.dispatch(resetSummaryState());
this.store.dispatch(stopUserPolling()); this.store.dispatch(stopUserPolling());
} }
} }

View File

@ -44,3 +44,5 @@ export const navigateToViewProcessorStatusHistory = createAction(
`${SUMMARY_LISTING_PREFIX} Navigate To Processor Status History`, `${SUMMARY_LISTING_PREFIX} Navigate To Processor Status History`,
props<{ id: string }>() props<{ id: string }>()
); );
export const resetSummaryState = createAction(`${SUMMARY_LISTING_PREFIX} Reset Summary State`);

View File

@ -16,13 +16,13 @@
*/ */
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { ProcessGroupStatusSnapshot, ProcessorStatusSnapshotEntity, SummaryListingState } from './index';
import { import {
AggregateSnapshot, loadSummaryListing,
ProcessGroupStatusSnapshot, loadSummaryListingSuccess,
ProcessorStatusSnapshotEntity, resetSummaryState,
SummaryListingState summaryListingApiError
} from './index'; } from './summary-listing.actions';
import { loadSummaryListing, loadSummaryListingSuccess, summaryListingApiError } from './summary-listing.actions';
export const initialState: SummaryListingState = { export const initialState: SummaryListingState = {
clusterSummary: null, clusterSummary: null,
@ -61,6 +61,10 @@ export const summaryListingReducer = createReducer(
...state, ...state,
error, error,
status: 'error' as const status: 'error' as const
})),
on(resetSummaryState, (state) => ({
...initialState
})) }))
); );

View File

@ -99,7 +99,7 @@
<ng-template #validNonService> <ng-template #validNonService>
<div [ngClass]="getNonServiceStateIcon(reference.component)"></div> <div [ngClass]="getNonServiceStateIcon(reference.component)"></div>
</ng-template> </ng-template>
<a [routerLink]="getRouteForReference(reference.component)" mat-dialog-close="ROUTED">{{ <a (click)="goToReferencingComponentClicked($event, reference.component)">{{
reference.component.name reference.component.name
}}</a> }}</a>
<div <div
@ -134,9 +134,11 @@
<ng-template #validService> <ng-template #validService>
<div [ngClass]="getServiceStateIcon(service.component)"></div> <div [ngClass]="getServiceStateIcon(service.component)"></div>
</ng-template> </ng-template>
<a [routerLink]="getRouteForReference(service.component)" mat-dialog-close="ROUTED">{{ <a
service.component.name (click)="goToReferencingComponentClicked($event, service.component)"
}}</a> mat-dialog-close="ROUTED"
>{{ service.component.name }}</a
>
<div <div
class="pointer fa fa-sticky-note-o" class="pointer fa fa-sticky-note-o"
*ngIf="hasBulletins(service)" *ngIf="hasBulletins(service)"

View File

@ -18,6 +18,7 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { import {
BulletinsTipInput, BulletinsTipInput,
ComponentType,
ControllerServiceReferencingComponent, ControllerServiceReferencingComponent,
ControllerServiceReferencingComponentEntity, ControllerServiceReferencingComponentEntity,
ValidationErrorsTipInput ValidationErrorsTipInput
@ -53,6 +54,7 @@ import { MatDialogModule } from '@angular/material/dialog';
}) })
export class ControllerServiceReferences { export class ControllerServiceReferences {
@Input() serviceReferences!: ControllerServiceReferencingComponentEntity[]; @Input() serviceReferences!: ControllerServiceReferencingComponentEntity[];
@Input() goToReferencingComponent!: (component: ControllerServiceReferencingComponent) => void;
protected readonly ValidationErrorsTip = ValidationErrorsTip; protected readonly ValidationErrorsTip = ValidationErrorsTip;
protected readonly BulletinsTip = BulletinsTip; protected readonly BulletinsTip = BulletinsTip;
@ -102,24 +104,9 @@ export class ControllerServiceReferences {
} }
} }
getRouteForReference(reference: ControllerServiceReferencingComponent): string[] { goToReferencingComponentClicked(event: MouseEvent, component: ControllerServiceReferencingComponent) {
if (reference.referenceType == 'ControllerService') { event.stopPropagation();
if (reference.groupId == null) { this.goToReferencingComponent(component);
return ['/settings', 'management-controller-services', reference.id];
} else {
return ['/process-groups', reference.groupId, 'controller-services', reference.id];
}
} else if (reference.referenceType == 'ReportingTask') {
return ['/settings', 'reporting-tasks', reference.id];
} else if (reference.referenceType == 'Processor') {
return ['/process-groups', reference.groupId, 'processors', reference.id];
} else if (reference.referenceType == 'FlowAnalysisRule') {
return ['/settings', 'flow-analysis-rules', reference.id];
} else if (reference.referenceType == 'ParameterProvider') {
return ['/settings', 'parameter-providers', reference.id];
} else {
return ['/settings', 'registry-clients', reference.id];
}
} }
hasBulletins(entity: ControllerServiceReferencingComponentEntity): boolean { hasBulletins(entity: ControllerServiceReferencingComponentEntity): boolean {

View File

@ -70,9 +70,8 @@
<div>Referencing Components</div> <div>Referencing Components</div>
<div> <div>
<controller-service-references <controller-service-references
[serviceReferences]=" [serviceReferences]="request.controllerService.component.referencingComponents"
request.controllerService.component.referencingComponents [goToReferencingComponent]="goToReferencingComponent"></controller-service-references>
"></controller-service-references>
</div> </div>
</div> </div>
</div> </div>

View File

@ -21,6 +21,7 @@ import { AbstractControl, FormBuilder, FormControl, FormGroup, ReactiveFormsModu
import { Client } from '../../../../service/client.service'; import { Client } from '../../../../service/client.service';
import { import {
ControllerServiceEntity, ControllerServiceEntity,
ControllerServiceReferencingComponent,
EditControllerServiceDialogRequest, EditControllerServiceDialogRequest,
InlineServiceCreationRequest, InlineServiceCreationRequest,
InlineServiceCreationResponse, InlineServiceCreationResponse,
@ -74,6 +75,7 @@ export class EditControllerService {
@Input() goToParameter!: (parameter: string) => void; @Input() goToParameter!: (parameter: string) => void;
@Input() convertToParameter!: (name: string, sensitive: boolean, value: string | null) => Observable<string>; @Input() convertToParameter!: (name: string, sensitive: boolean, value: string | null) => Observable<string>;
@Input() goToService!: (serviceId: string) => void; @Input() goToService!: (serviceId: string) => void;
@Input() goToReferencingComponent!: (component: ControllerServiceReferencingComponent) => void;
@Input() saving$!: Observable<boolean>; @Input() saving$!: Observable<boolean>;
@Output() editControllerService: EventEmitter<UpdateControllerServiceRequest> = @Output() editControllerService: EventEmitter<UpdateControllerServiceRequest> =
new EventEmitter<UpdateControllerServiceRequest>(); new EventEmitter<UpdateControllerServiceRequest>();