NIFI-13001: Allowing the user to acknowledge changes to the cluster connection state (#8606)

* NIFI-13001:
- Allowing the user to acknowledge changes to the cluster connection state.

* NIFI-13001:
- Addressing review feedback.

This closes #8606
This commit is contained in:
Matt Gilman 2024-04-12 12:05:59 -04:00 committed by GitHub
parent cc7af91f97
commit 36d95c5c3b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 650 additions and 136 deletions

View File

@ -19,6 +19,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state';
import { startCurrentUserPolling, stopCurrentUserPolling } from '../../../state/current-user/current-user.actions';
import {
loadClusterSummary,
startClusterSummaryPolling,
stopClusterSummaryPolling
} from '../../../state/cluster-summary/cluster-summary.actions';
@Component({
selector: 'access-policies',
@ -29,10 +34,13 @@ export class AccessPolicies implements OnInit, OnDestroy {
constructor(private store: Store<NiFiState>) {}
ngOnInit(): void {
this.store.dispatch(loadClusterSummary());
this.store.dispatch(startCurrentUserPolling());
this.store.dispatch(startClusterSummaryPolling());
}
ngOnDestroy(): void {
this.store.dispatch(stopClusterSummaryPolling());
this.store.dispatch(stopCurrentUserPolling());
}
}

View File

@ -16,12 +16,13 @@
*/
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { Observable } from 'rxjs';
import { AccessPolicyEntity, ComponentResourceAction, ResourceAction } from '../state/shared';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { TenantEntity } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class AccessPolicyService {
@ -30,7 +31,8 @@ export class AccessPolicyService {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
createAccessPolicy(
@ -53,6 +55,7 @@ export class AccessPolicyService {
version: 0,
clientId: this.client.getClientId()
},
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
action: resourceAction.action,
resource,
@ -81,6 +84,7 @@ export class AccessPolicyService {
updateAccessPolicy(accessPolicy: AccessPolicyEntity, users: TenantEntity[], userGroups: TenantEntity[]) {
const payload: unknown = {
revision: this.client.getRevision(accessPolicy),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: accessPolicy.id,
userGroups,
@ -92,8 +96,13 @@ export class AccessPolicyService {
}
deleteAccessPolicy(accessPolicy: AccessPolicyEntity): Observable<any> {
const revision: any = this.client.getRevision(accessPolicy);
return this.httpClient.delete(this.nifiCommon.stripProtocol(accessPolicy.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(accessPolicy),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(accessPolicy.uri), { params });
}
getUsers(): Observable<any> {

View File

@ -28,6 +28,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MoveComponentRequest, UpdateComponentRequest } from '../../state/flow';
import { Position } from '../../state/shared';
import { ComponentType } from '../../../../state/shared';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
@Injectable({
providedIn: 'root'
@ -44,7 +45,8 @@ export class DraggableBehavior {
constructor(
private store: Store<CanvasState>,
private client: Client,
private canvasUtils: CanvasUtils
private canvasUtils: CanvasUtils,
private clusterConnectionService: ClusterConnectionService
) {
const self: DraggableBehavior = this;
@ -247,7 +249,7 @@ export class DraggableBehavior {
*
* @param {selection} the destination group
*/
private updateComponentsGroup(group: any) {
private updateComponentsGroup(group: any): void {
// get the selection and deselect the components being moved
const selection: any = d3.selectAll('g.component.selected, g.connection.selected').classed('selected', false);
@ -308,6 +310,7 @@ export class DraggableBehavior {
uri: d.uri,
payload: {
revision: this.client.getRevision(d),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: d.id,
position: newPosition
@ -346,6 +349,7 @@ export class DraggableBehavior {
uri: connection.uri,
payload: {
revision: this.client.getRevision(connection),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: connection.id,
bends: newBends

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { EMPTY, Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import {
@ -27,6 +27,7 @@ import {
PropertyDescriptorRetriever
} from '../../../state/shared';
import { ConfigureControllerServiceRequest, DeleteControllerServiceRequest } from '../state/controller-services';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ControllerServiceService implements ControllerServiceCreator, PropertyDescriptorRetriever {
@ -35,7 +36,8 @@ export class ControllerServiceService implements ControllerServiceCreator, Prope
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getControllerServices(processGroupId: string): Observable<any> {
@ -66,6 +68,7 @@ export class ControllerServiceService implements ControllerServiceCreator, Prope
`${ControllerServiceService.API}/process-groups/${processGroupId}/controller-services`,
{
revision: createControllerService.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
bundle: createControllerService.controllerServiceBundle,
type: createControllerService.controllerServiceType
@ -95,7 +98,12 @@ export class ControllerServiceService implements ControllerServiceCreator, Prope
deleteControllerService(deleteControllerService: DeleteControllerServiceRequest): Observable<any> {
const entity: ControllerServiceEntity = deleteControllerService.controllerService;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
}

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import {
ComponentRunStatusRequest,
CreateComponentRequest,
@ -47,6 +47,7 @@ import {
import { ComponentType, PropertyDescriptorRetriever } from '../../../state/shared';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class FlowService implements PropertyDescriptorRetriever {
@ -55,7 +56,8 @@ export class FlowService implements PropertyDescriptorRetriever {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getFlow(processGroupId = 'root'): Observable<any> {
@ -112,6 +114,7 @@ export class FlowService implements PropertyDescriptorRetriever {
createFunnel(processGroupId = 'root', createFunnel: CreateComponentRequest): Observable<any> {
return this.httpClient.post(`${FlowService.API}/process-groups/${processGroupId}/funnels`, {
revision: createFunnel.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createFunnel.position
}
@ -121,6 +124,7 @@ export class FlowService implements PropertyDescriptorRetriever {
createLabel(processGroupId = 'root', createLabel: CreateComponentRequest): Observable<any> {
return this.httpClient.post(`${FlowService.API}/process-groups/${processGroupId}/labels`, {
revision: createLabel.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createLabel.position
}
@ -134,6 +138,7 @@ export class FlowService implements PropertyDescriptorRetriever {
createProcessor(processGroupId = 'root', createProcessor: CreateProcessorRequest): Observable<any> {
return this.httpClient.post(`${FlowService.API}/process-groups/${processGroupId}/processors`, {
revision: createProcessor.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createProcessor.position,
type: createProcessor.processorType,
@ -152,6 +157,7 @@ export class FlowService implements PropertyDescriptorRetriever {
createProcessGroup(processGroupId = 'root', createProcessGroup: CreateProcessGroupRequest): Observable<any> {
const payload: any = {
revision: createProcessGroup.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createProcessGroup.position,
name: createProcessGroup.name
@ -173,6 +179,7 @@ export class FlowService implements PropertyDescriptorRetriever {
): Observable<any> {
const payload: any = {
revision: createRemoteProcessGroup.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createRemoteProcessGroup.position,
targetUris: createRemoteProcessGroup.targetUris,
@ -200,6 +207,10 @@ export class FlowService implements PropertyDescriptorRetriever {
payload.append('positionX', uploadProcessGroup.position.x.toString());
payload.append('positionY', uploadProcessGroup.position.y.toString());
payload.append('clientId', uploadProcessGroup.revision.clientId);
payload.append(
'disconnectedNodeAcknowledged',
String(this.clusterConnectionService.isDisconnectionAcknowledged())
);
payload.append('file', uploadProcessGroup.flowDefinition);
return this.httpClient.post(
@ -222,6 +233,7 @@ export class FlowService implements PropertyDescriptorRetriever {
const portType: string = ComponentType.InputPort == createPort.type ? 'input-ports' : 'output-ports';
return this.httpClient.post(`${FlowService.API}/process-groups/${processGroupId}/${portType}`, {
revision: createPort.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: createPort.position,
name: createPort.name,
@ -235,17 +247,25 @@ export class FlowService implements PropertyDescriptorRetriever {
}
deleteComponent(deleteComponent: DeleteComponentRequest): Observable<any> {
const revision: any = this.client.getRevision(deleteComponent.entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(deleteComponent.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(deleteComponent.entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(deleteComponent.uri), { params });
}
createSnippet(snippet: Snippet): Observable<any> {
return this.httpClient.post(`${FlowService.API}/snippets`, { snippet });
return this.httpClient.post(`${FlowService.API}/snippets`, {
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
snippet
});
}
moveSnippet(snippetId: string, groupId: string): Observable<any> {
const payload: any = {
// 'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
snippet: {
id: snippetId,
parentGroupId: groupId
@ -255,7 +275,12 @@ export class FlowService implements PropertyDescriptorRetriever {
}
deleteSnippet(snippetId: string): Observable<any> {
return this.httpClient.delete(`${FlowService.API}/snippets/${snippetId}`);
const params = new HttpParams({
fromObject: {
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(`${FlowService.API}/snippets/${snippetId}`, { params });
}
replayLastProvenanceEvent(request: ReplayLastProvenanceEventRequest): Observable<any> {
@ -265,7 +290,7 @@ export class FlowService implements PropertyDescriptorRetriever {
runOnce(request: RunOnceRequest): Observable<any> {
const startRequest: ComponentRunStatusRequest = {
revision: request.revision,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'RUN_ONCE'
};
return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, startRequest);
@ -274,7 +299,7 @@ export class FlowService implements PropertyDescriptorRetriever {
startComponent(request: StartComponentRequest): Observable<any> {
const startRequest: ComponentRunStatusRequest = {
revision: request.revision,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: request.type === ComponentType.RemoteProcessGroup ? 'TRANSMITTING' : 'RUNNING'
};
return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, startRequest);
@ -283,7 +308,7 @@ export class FlowService implements PropertyDescriptorRetriever {
stopComponent(request: StopComponentRequest): Observable<any> {
const stopRequest: ComponentRunStatusRequest = {
revision: request.revision,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'STOPPED'
};
return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, stopRequest);
@ -292,7 +317,7 @@ export class FlowService implements PropertyDescriptorRetriever {
startProcessGroup(request: StartProcessGroupRequest): Observable<any> {
const startRequest: ProcessGroupRunStatusRequest = {
id: request.id,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'RUNNING'
};
return this.httpClient.put(`${FlowService.API}/flow/process-groups/${request.id}`, startRequest);
@ -300,7 +325,7 @@ export class FlowService implements PropertyDescriptorRetriever {
startRemoteProcessGroupsInProcessGroup(request: StartProcessGroupRequest): Observable<any> {
const startRequest = {
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'TRANSMITTING'
};
return this.httpClient.put(
@ -312,7 +337,7 @@ export class FlowService implements PropertyDescriptorRetriever {
stopProcessGroup(request: StopProcessGroupRequest): Observable<any> {
const stopRequest: ProcessGroupRunStatusRequest = {
id: request.id,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'STOPPED'
};
return this.httpClient.put(`${FlowService.API}/flow/process-groups/${request.id}`, stopRequest);
@ -320,7 +345,7 @@ export class FlowService implements PropertyDescriptorRetriever {
stopRemoteProcessGroupsInProcessGroup(request: StopProcessGroupRequest): Observable<any> {
const stopRequest = {
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'STOPPED'
};
return this.httpClient.put(
@ -338,7 +363,7 @@ export class FlowService implements PropertyDescriptorRetriever {
saveToFlowRegistry(request: SaveToVersionControlRequest): Observable<VersionControlInformationEntity> {
const saveRequest = {
...request,
disconnectedNodeAcknowledged: false
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
};
return this.httpClient.post(
@ -351,7 +376,7 @@ export class FlowService implements PropertyDescriptorRetriever {
const params: any = {
version: request.revision.version,
clientId: request.revision.clientId,
disconnectedNodeAcknowledged: false
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
};
return this.httpClient.delete(`${FlowService.API}/versions/process-groups/${request.processGroupId}`, {
params

View File

@ -36,6 +36,7 @@ import { selectFlowConfiguration } from '../../../../state/flow-configuration/fl
import * as fromFlowConfiguration from '../../../../state/flow-configuration/flow-configuration.reducer';
import { queueFeatureKey } from '../../../queue/state';
import * as fromQueue from '../../state/queue/queue.reducer';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
describe('ConnectionManager', () => {
let service: ConnectionManager;
@ -71,7 +72,13 @@ describe('ConnectionManager', () => {
value: fromFlowConfiguration.initialState.flowConfiguration
}
]
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
service = TestBed.inject(ConnectionManager);

View File

@ -47,6 +47,7 @@ import { loadBalanceStrategies, UpdateComponentRequest } from '../../state/flow'
import { filter, switchMap } from 'rxjs';
import { NiFiCommon } from '../../../../service/nifi-common.service';
import { QuickSelectBehavior } from '../behavior/quick-select-behavior.service';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export class ConnectionRenderOptions {
updatePath?: boolean;
@ -98,7 +99,8 @@ export class ConnectionManager {
private client: Client,
private selectableBehavior: SelectableBehavior,
private transitionBehavior: TransitionBehavior,
private quickSelectBehavior: QuickSelectBehavior
private quickSelectBehavior: QuickSelectBehavior,
private clusterConnectionService: ClusterConnectionService
) {}
/**
@ -350,6 +352,7 @@ export class ConnectionManager {
uri: d.uri,
payload: {
revision: this.client.getRevision(d),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: connection
},
restoreOnFailure: restoreOnFailure
@ -1974,7 +1977,7 @@ export class ConnectionManager {
const payload: any = {
revision: self.client.getRevision(connectionData),
// TODO - 'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
disconnectedNodeAcknowledged: self.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: connectionData.id,
destination: {

View File

@ -36,6 +36,7 @@ import { selectFlowConfiguration } from '../../../../state/flow-configuration/fl
import * as fromFlowConfiguration from '../../../../state/flow-configuration/flow-configuration.reducer';
import { queueFeatureKey } from '../../../queue/state';
import * as fromQueue from '../../state/queue/queue.reducer';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
describe('LabelManager', () => {
let service: LabelManager;
@ -71,7 +72,13 @@ describe('LabelManager', () => {
value: fromFlowConfiguration.initialState.flowConfiguration
}
]
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
service = TestBed.inject(LabelManager);

View File

@ -37,6 +37,7 @@ import { ComponentType } from '../../../../state/shared';
import { UpdateComponentRequest } from '../../state/flow';
import { filter, switchMap } from 'rxjs';
import { NiFiCommon } from '../../../../service/nifi-common.service';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
@Injectable({
providedIn: 'root'
@ -62,6 +63,7 @@ export class LabelManager {
private canvasUtils: CanvasUtils,
private nifiCommon: NiFiCommon,
private client: Client,
private clusterConnectionService: ClusterConnectionService,
private positionBehavior: PositionBehavior,
private selectableBehavior: SelectableBehavior,
private quickSelectBehavior: QuickSelectBehavior,
@ -343,6 +345,7 @@ export class LabelManager {
uri: labelData.uri,
payload: {
revision: self.client.getRevision(labelData),
disconnectedNodeAcknowledged: self.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: labelData.id,
width: labelData.dimensions.width,

View File

@ -31,6 +31,7 @@ import * as ErrorActions from '../../../state/error/error.actions';
import * as ParameterActions from '../state/parameter/parameter.actions';
import { FlowService } from './flow.service';
import { MEDIUM_DIALOG } from '../../../index';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({
providedIn: 'root'
@ -41,6 +42,7 @@ export class ParameterHelperService {
private store: Store<NiFiState>,
private flowService: FlowService,
private parameterService: ParameterService,
private clusterConnectionService: ClusterConnectionService,
private client: Client
) {}
@ -120,6 +122,8 @@ export class ParameterHelperService {
id: parameterContextId,
payload: {
revision: this.client.getRevision(parameterContextEntity),
disconnectedNodeAcknowledged:
this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: parameterContextEntity.id,
parameters: [{ parameter: dialogResponse.parameter }]

View File

@ -17,9 +17,10 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { ParameterContextUpdateRequest, SubmitParameterContextUpdate } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ParameterService {
@ -27,7 +28,8 @@ export class ParameterService {
constructor(
private httpClient: HttpClient,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getParameterContext(id: string, includeInheritedParameters: boolean): Observable<any> {
@ -50,6 +52,11 @@ export class ParameterService {
}
deleteParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable<any> {
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri));
const params = new HttpParams({
fromObject: {
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri), { params });
}
}

View File

@ -26,6 +26,7 @@ import { selectService } from '../../state/controller-services/controller-servic
import { ControllerServiceService } from '../controller-service.service';
import { HttpErrorResponse } from '@angular/common/http';
import { fullScreenError } from '../../../../state/error/error.actions';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (
route: ActivatedRouteSnapshot
@ -33,6 +34,7 @@ export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
const store: Store<NiFiState> = inject(Store);
const controllerServiceService: ControllerServiceService = inject(ControllerServiceService);
const client: Client = inject(Client);
const clusterConnectionService: ClusterConnectionService = inject(ClusterConnectionService);
// getting id parameter from activated route because ngrx router store
// is not initialized when this resolver executes
@ -72,7 +74,7 @@ export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
clientId: revision.clientId,
revision: revision.version,
editable,
disconnectedNodeAcknowledged: false // TODO
disconnectedNodeAcknowledged: clusterConnectionService.isDisconnectionAcknowledged()
} as AdvancedUiParams;
}),
take(1)

View File

@ -26,11 +26,13 @@ import { AdvancedUiParams } from '../../../../state/shared';
import { Client } from '../../../../service/client.service';
import { fullScreenError } from '../../../../state/error/error.actions';
import { HttpErrorResponse } from '@angular/common/http';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export const processorAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (route: ActivatedRouteSnapshot) => {
const store: Store<NiFiState> = inject(Store);
const flowService: FlowService = inject(FlowService);
const client: Client = inject(Client);
const clusterConnectionService: ClusterConnectionService = inject(ClusterConnectionService);
// getting id parameter from activated route because ngrx router store
// is not initialized when this resolver executes
@ -73,7 +75,7 @@ export const processorAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (r
clientId: revision.clientId,
revision: revision.version,
editable,
disconnectedNodeAcknowledged: false // TODO
disconnectedNodeAcknowledged: clusterConnectionService.isDisconnectionAcknowledged()
} as AdvancedUiParams;
}),
take(1)

View File

@ -35,6 +35,7 @@ import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared';
import { selectTimeOffset } from '../../../../state/flow-configuration/flow-configuration.selectors';
import { selectAbout } from '../../../../state/about/about.selectors';
import { MEDIUM_DIALOG } from '../../../../index';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
@Injectable()
export class ManageRemotePortsEffects {
@ -44,7 +45,8 @@ export class ManageRemotePortsEffects {
private manageRemotePortService: ManageRemotePortService,
private errorHelper: ErrorHelper,
private dialog: MatDialog,
private router: Router
private router: Router,
private clusterConnectionService: ClusterConnectionService
) {}
loadRemotePorts$ = createEffect(() =>
@ -136,7 +138,7 @@ export class ManageRemotePortsEffects {
.updateRemotePortTransmission({
portId: request.port.id,
rpg: request.rpg,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
type: request.port.type,
state: 'TRANSMITTING'
})
@ -165,7 +167,7 @@ export class ManageRemotePortsEffects {
.updateRemotePortTransmission({
portId: request.port.id,
rpg: request.rpg,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
type: request.port.type,
state: 'STOPPED'
})

View File

@ -36,9 +36,17 @@ export class FlowStatus {
@Input() controllerStatus: ControllerStatus = initialState.flowStatus.controllerStatus;
@Input() lastRefreshed: string = initialState.flow.processGroupFlow.lastRefreshed;
@Input() clusterSummary: ClusterSummary | null = null;
@Input() bulletins: BulletinEntity[] = initialState.controllerBulletins.bulletins;
@Input() currentProcessGroupId: string = initialState.id;
@Input() loadingStatus = false;
@Input() set bulletins(bulletins: BulletinEntity[]) {
if (bulletins) {
this.filteredBulletins = bulletins.filter((bulletin) => bulletin.canRead);
} else {
this.filteredBulletins = [];
}
}
private filteredBulletins: BulletinEntity[] = initialState.controllerBulletins.bulletins;
protected readonly BulletinsTip = BulletinsTip;
@ -112,12 +120,12 @@ export class FlowStatus {
}
hasBulletins(): boolean {
return this.bulletins.length > 0;
return this.filteredBulletins.length > 0;
}
getBulletins(): BulletinsTipInput {
return {
bulletins: this.bulletins
bulletins: this.filteredBulletins
};
}

View File

@ -25,6 +25,7 @@ import { CreateConnectionDialogRequest } from '../../../../../state/flow';
import { ComponentType, DocumentedType } from '../../../../../../../state/shared';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { of } from 'rxjs';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
describe('CreateConnection', () => {
let component: CreateConnection;
@ -367,7 +368,16 @@ describe('CreateConnection', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CreateConnection, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ initialState }),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(CreateConnection);
component = fixture.componentInstance;

View File

@ -45,7 +45,6 @@ import { SourceProcessor } from '../source/source-processor/source-processor.com
import { DestinationFunnel } from '../destination/destination-funnel/destination-funnel.component';
import { createConnection } from '../../../../../state/flow/flow.actions';
import { Client } from '../../../../../../../service/client.service';
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
import { SourceFunnel } from '../source/source-funnel/source-funnel.component';
import { DestinationProcessor } from '../destination/destination-processor/destination-processor.component';
import { DestinationOutputPort } from '../destination/destination-output-port/destination-output-port.component';
@ -56,6 +55,8 @@ import { DestinationProcessGroup } from '../destination/destination-process-grou
import { SourceRemoteProcessGroup } from '../source/source-remote-process-group/source-remote-process-group.component';
import { DestinationRemoteProcessGroup } from '../destination/destination-remote-process-group/destination-remote-process-group.component';
import { BreadcrumbEntity } from '../../../../../state/shared';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
@Component({
selector: 'create-connection',
@ -114,6 +115,8 @@ export class CreateConnection {
}
}
protected readonly loadBalanceStrategies = loadBalanceStrategies;
protected readonly loadBalanceCompressionStrategies = loadBalanceCompressionStrategies;
protected readonly ComponentType = ComponentType;
protected readonly TextTip = TextTip;
@ -136,6 +139,7 @@ export class CreateConnection {
private formBuilder: FormBuilder,
private store: Store<NiFiState>,
private canvasUtils: CanvasUtils,
private clusterConnectionService: ClusterConnectionService,
private client: Client
) {
this.source = dialogRequest.request.source;
@ -230,6 +234,7 @@ export class CreateConnection {
version: 0,
clientId: this.client.getClientId()
},
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
backPressureDataSizeThreshold: this.createConnectionForm.get('backPressureDataSizeThreshold')?.value,
backPressureObjectThreshold: this.createConnectionForm.get('backPressureObjectThreshold')?.value,
@ -299,7 +304,4 @@ export class CreateConnection {
})
);
}
protected readonly loadBalanceStrategies = loadBalanceStrategies;
protected readonly loadBalanceCompressionStrategies = loadBalanceCompressionStrategies;
}

View File

@ -25,6 +25,7 @@ import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EMPTY } from 'rxjs';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
describe('ImportFromRegistry', () => {
let component: ImportFromRegistry;
@ -112,7 +113,16 @@ describe('ImportFromRegistry', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ImportFromRegistry, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ initialState }),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(ImportFromRegistry);
component = fixture.componentInstance;

View File

@ -53,6 +53,7 @@ import { selectTimeOffset } from '../../../../../../../state/flow-configuration/
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Client } from '../../../../../../../service/client.service';
import { importFromRegistry } from '../../../../../state/flow/flow.actions';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
@Component({
selector: 'import-from-registry',
@ -117,7 +118,8 @@ export class ImportFromRegistry implements OnInit {
private formBuilder: FormBuilder,
private store: Store<CanvasState>,
private nifiCommon: NiFiCommon,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
this.store
.select(selectTimeOffset)
@ -309,7 +311,7 @@ export class ImportFromRegistry implements OnInit {
version: 0
}
}),
// disconnectedNodeAcknowledged: nfStorage.isDisconnectionAcknowledged(),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
position: {
x: this.dialogRequest.request.position.x,

View File

@ -24,6 +24,7 @@ import { ComponentType } from '../../../../../../../state/shared';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/flow/flow.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
describe('EditPort', () => {
let component: EditPort;
@ -96,7 +97,16 @@ describe('EditPort', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditPort, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ initialState }),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditPort);
component = fixture.componentInstance;

View File

@ -31,6 +31,7 @@ import { MatButtonModule } from '@angular/material/button';
import { AsyncPipe } from '@angular/common';
import { selectSaving } from '../../../../../state/flow/flow.selectors';
import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nifi-spinner.directive';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-port',
@ -58,7 +59,8 @@ export class EditPort {
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
private formBuilder: FormBuilder,
private store: Store<CanvasState>,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
// set the port type name
if (ComponentType.InputPort == this.request.type) {
@ -82,6 +84,7 @@ export class EditPort {
editPort() {
const payload: any = {
revision: this.client.getRevision(this.request.entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.entity.id,
name: this.editPortForm.get('name')?.value,

View File

@ -20,6 +20,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditProcessGroup } from './edit-process-group.component';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
describe('EditProcessGroup', () => {
let component: EditProcessGroup;
@ -107,7 +108,15 @@ describe('EditProcessGroup', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditProcessGroup, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditProcessGroup);
component = fixture.componentInstance;

View File

@ -35,6 +35,7 @@ import { TextTip } from '../../../../../../../ui/common/tooltips/text-tip/text-t
import { ParameterContextEntity } from '../../../../../../parameter-contexts/state/parameter-context-listing';
import { ControllerServiceTable } from '../../../../../../../ui/common/controller-service/controller-service-table/controller-service-table.component';
import { EditComponentDialogRequest } from '../../../../../state/flow';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-process-group',
@ -151,7 +152,8 @@ export class EditProcessGroup {
constructor(
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
private formBuilder: FormBuilder,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
this.parameterContextsOptions.push({
text: 'No parameter context',
@ -199,6 +201,7 @@ export class EditProcessGroup {
const payload: any = {
revision: this.client.getRevision(this.request.entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
processGroupUpdateStrategy: updateStrategy,
component: {
id: this.request.entity.id,

View File

@ -25,6 +25,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../../../state/error/error.reducer';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
describe('EditProcessor', () => {
let component: EditProcessor;
@ -736,7 +737,13 @@ describe('EditProcessor', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditProcessor);

View File

@ -48,6 +48,7 @@ import {
RelationshipSettings
} from './relationship-settings/relationship-settings.component';
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-processor',
@ -147,6 +148,7 @@ export class EditProcessor {
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
private formBuilder: FormBuilder,
private client: Client,
private clusterConnectionService: ClusterConnectionService,
private nifiCommon: NiFiCommon
) {
const processorProperties: any = request.entity.component.config.properties;
@ -291,6 +293,7 @@ export class EditProcessor {
const payload: any = {
revision: this.client.getRevision(this.request.entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.entity.id,
name: this.editProcessorForm.get('name')?.value,

View File

@ -24,6 +24,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { EditComponentDialogRequest } from '../../../state/flow';
import { ComponentType } from '../../../../../state/shared';
import { initialState } from '../../../state/manage-remote-ports/manage-remote-ports.reducer';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('EditRemotePortComponent', () => {
let component: EditRemotePortComponent;
@ -50,7 +51,16 @@ describe('EditRemotePortComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditRemotePortComponent, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ initialState }),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditRemotePortComponent);
component = fixture.componentInstance;

View File

@ -31,6 +31,7 @@ import { Client } from '../../../../../service/client.service';
import { ComponentType } from '../../../../../state/shared';
import { PortSummary } from '../../../state/manage-remote-ports';
import { configureRemotePort } from '../../../state/manage-remote-ports/manage-remote-ports.actions';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
standalone: true,
@ -57,7 +58,8 @@ export class EditRemotePortComponent {
@Inject(MAT_DIALOG_DATA) public request: EditRemotePortDialogRequest,
private formBuilder: FormBuilder,
private store: Store<CanvasState>,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
// set the port type name
if (ComponentType.InputPort == this.request.type) {
@ -79,7 +81,7 @@ export class EditRemotePortComponent {
editRemotePort() {
const payload: any = {
revision: this.client.getRevision(this.request.rpg),
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
type: this.request.type,
remoteProcessGroupPort: {
concurrentlySchedulableTaskCount: this.editPortForm.get('concurrentTasks')?.value,

View File

@ -19,6 +19,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state';
import { startCurrentUserPolling, stopCurrentUserPolling } from '../../../state/current-user/current-user.actions';
import {
loadClusterSummary,
startClusterSummaryPolling,
stopClusterSummaryPolling
} from '../../../state/cluster-summary/cluster-summary.actions';
@Component({
selector: 'parameter-contexts',
@ -29,10 +34,13 @@ export class ParameterContexts implements OnInit, OnDestroy {
constructor(private store: Store<NiFiState>) {}
ngOnInit(): void {
this.store.dispatch(loadClusterSummary());
this.store.dispatch(startCurrentUserPolling());
this.store.dispatch(startClusterSummaryPolling());
}
ngOnDestroy(): void {
this.store.dispatch(stopClusterSummaryPolling());
this.store.dispatch(stopCurrentUserPolling());
}
}

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import {
@ -26,6 +26,7 @@ import {
ParameterContextEntity
} from '../state/parameter-context-listing';
import { ParameterContextUpdateRequest, SubmitParameterContextUpdate } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ParameterContextService {
@ -34,7 +35,8 @@ export class ParameterContextService {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getParameterContexts(): Observable<any> {
@ -68,14 +70,22 @@ export class ParameterContextService {
}
deleteParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable<any> {
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri));
const params = new HttpParams({
fromObject: {
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri), { params });
}
deleteParameterContext(deleteParameterContext: DeleteParameterContextRequest): Observable<any> {
const entity: ParameterContextEntity = deleteParameterContext.parameterContext;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(`${ParameterContextService.API}/parameter-contexts/${entity.id}`, {
params: revision
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(`${ParameterContextService.API}/parameter-contexts/${entity.id}`, { params });
}
}

View File

@ -24,6 +24,7 @@ import { of } from 'rxjs';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../state/parameter-context-listing/parameter-context-listing.reducer';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('EditParameterContext', () => {
let component: EditParameterContext;
@ -235,7 +236,16 @@ describe('EditParameterContext', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [EditParameterContext, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({ initialState }),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditParameterContext);
component = fixture.componentInstance;

View File

@ -41,6 +41,7 @@ import { ParameterContextInheritance } from '../parameter-context-inheritance/pa
import { ParameterReferences } from '../../../../../ui/common/parameter-references/parameter-references.component';
import { RouterLink } from '@angular/router';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-parameter-context',
@ -86,7 +87,8 @@ export class EditParameterContext {
constructor(
@Inject(MAT_DIALOG_DATA) public request: EditParameterContextRequest,
private formBuilder: FormBuilder,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
if (request.parameterContext) {
this.isNew = false;
@ -131,6 +133,7 @@ export class EditParameterContext {
version: 0,
clientId: this.client.getClientId()
},
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
name: this.editParameterContextForm.get('name')?.value,
description: this.editParameterContextForm.get('description')?.value,
@ -152,6 +155,7 @@ export class EditParameterContext {
const payload: any = {
revision: this.client.getRevision(pc),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: pc.id,
name: this.editParameterContextForm.get('name')?.value,

View File

@ -20,6 +20,11 @@ import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state';
import { startCurrentUserPolling, stopCurrentUserPolling } from '../../../state/current-user/current-user.actions';
import { loadExtensionTypesForSettings } from '../../../state/extension-types/extension-types.actions';
import {
loadClusterSummary,
startClusterSummaryPolling,
stopClusterSummaryPolling
} from '../../../state/cluster-summary/cluster-summary.actions';
@Component({
selector: 'settings',
@ -57,11 +62,14 @@ export class Settings implements OnInit, OnDestroy {
constructor(private store: Store<NiFiState>) {}
ngOnInit(): void {
this.store.dispatch(loadClusterSummary());
this.store.dispatch(startCurrentUserPolling());
this.store.dispatch(startClusterSummaryPolling());
this.store.dispatch(loadExtensionTypesForSettings());
}
ngOnDestroy(): void {
this.store.dispatch(stopClusterSummaryPolling());
this.store.dispatch(stopCurrentUserPolling());
}
}

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import {
@ -28,6 +28,7 @@ import {
FlowAnalysisRuleEntity
} from '../state/flow-analysis-rules';
import { PropertyDescriptorRetriever } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class FlowAnalysisRuleService implements PropertyDescriptorRetriever {
@ -36,7 +37,8 @@ export class FlowAnalysisRuleService implements PropertyDescriptorRetriever {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getFlowAnalysisRule(): Observable<any> {
@ -46,6 +48,7 @@ export class FlowAnalysisRuleService implements PropertyDescriptorRetriever {
createFlowAnalysisRule(createFlowAnalysisRule: CreateFlowAnalysisRuleRequest): Observable<any> {
return this.httpClient.post(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`, {
revision: createFlowAnalysisRule.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
bundle: createFlowAnalysisRule.flowAnalysisRuleBundle,
type: createFlowAnalysisRule.flowAnalysisRuleType
@ -55,8 +58,13 @@ export class FlowAnalysisRuleService implements PropertyDescriptorRetriever {
deleteFlowAnalysisRule(deleteFlowAnalysisRule: DeleteFlowAnalysisRuleRequest): Observable<any> {
const entity: FlowAnalysisRuleEntity = deleteFlowAnalysisRule.flowAnalysisRule;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable<any> {
@ -80,6 +88,7 @@ export class FlowAnalysisRuleService implements PropertyDescriptorRetriever {
const entity: FlowAnalysisRuleEntity = flowAnalysisRule.flowAnalysisRule;
return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, {
revision: this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: enabled ? 'ENABLED' : 'DISABLED',
uiOnly: true
});

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import {
ConfigureControllerServiceRequest,
DeleteControllerServiceRequest
@ -30,6 +30,7 @@ import {
CreateControllerServiceRequest,
PropertyDescriptorRetriever
} from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ManagementControllerServiceService implements ControllerServiceCreator, PropertyDescriptorRetriever {
@ -38,7 +39,8 @@ export class ManagementControllerServiceService implements ControllerServiceCrea
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getControllerServices(): Observable<any> {
@ -51,6 +53,7 @@ export class ManagementControllerServiceService implements ControllerServiceCrea
createControllerService(createControllerService: CreateControllerServiceRequest): Observable<any> {
return this.httpClient.post(`${ManagementControllerServiceService.API}/controller/controller-services`, {
revision: createControllerService.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
bundle: createControllerService.controllerServiceBundle,
type: createControllerService.controllerServiceType
@ -84,7 +87,12 @@ export class ManagementControllerServiceService implements ControllerServiceCrea
deleteControllerService(deleteControllerService: DeleteControllerServiceRequest): Observable<any> {
const entity: ControllerServiceEntity = deleteControllerService.controllerService;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
}

View File

@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import { Observable } from 'rxjs';
@ -30,6 +30,7 @@ import {
ParameterProviderParameterApplicationEntity
} from '../state/parameter-providers';
import { PropertyDescriptorRetriever } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ParameterProviderService implements PropertyDescriptorRetriever {
@ -38,7 +39,8 @@ export class ParameterProviderService implements PropertyDescriptorRetriever {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getParameterProviders(): Observable<any> {
@ -52,6 +54,7 @@ export class ParameterProviderService implements PropertyDescriptorRetriever {
createParameterProvider(request: CreateParameterProviderRequest) {
return this.httpClient.post(`${ParameterProviderService.API}/controller/parameter-providers`, {
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
bundle: request.parameterProviderBundle,
type: request.parameterProviderType
@ -61,11 +64,12 @@ export class ParameterProviderService implements PropertyDescriptorRetriever {
deleteParameterProvider(request: DeleteParameterProviderRequest) {
const entity: ParameterProviderEntity = request.parameterProvider;
const revision: any = this.client.getRevision(entity);
const params: any = {
...revision,
disconnectedNodeAcknowledged: false
};
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
@ -88,9 +92,10 @@ export class ParameterProviderService implements PropertyDescriptorRetriever {
`${ParameterProviderService.API}/parameter-providers/${request.id}/parameters/fetch-requests`,
{
id: request.id,
revision: request.revision
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
},
{ params: { disconnectedNodeAcknowledged: false } }
{ params: { disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged() } }
);
}
@ -110,6 +115,11 @@ export class ParameterProviderService implements PropertyDescriptorRetriever {
deleteParameterProviderParametersUpdateRequest(
updateRequest: ParameterProviderApplyParametersRequest
): Observable<any> {
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri));
const params = new HttpParams({
fromObject: {
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri), { params });
}
}

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import {
@ -26,6 +26,7 @@ import {
EditRegistryClientRequest
} from '../state/registry-clients';
import { PropertyDescriptorRetriever, RegistryClientEntity } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class RegistryClientService implements PropertyDescriptorRetriever {
@ -34,7 +35,8 @@ export class RegistryClientService implements PropertyDescriptorRetriever {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getRegistryClients(): Observable<any> {
@ -61,7 +63,12 @@ export class RegistryClientService implements PropertyDescriptorRetriever {
deleteRegistryClient(deleteRegistryClient: DeleteRegistryClientRequest): Observable<any> {
const entity: RegistryClientEntity = deleteRegistryClient.registryClient;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
}

View File

@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { NiFiCommon } from '../../../service/nifi-common.service';
import {
@ -29,6 +29,7 @@ import {
StopReportingTaskRequest
} from '../state/reporting-tasks';
import { PropertyDescriptorRetriever } from '../../../state/shared';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ReportingTaskService implements PropertyDescriptorRetriever {
@ -37,7 +38,8 @@ export class ReportingTaskService implements PropertyDescriptorRetriever {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getReportingTasks(): Observable<any> {
@ -51,6 +53,7 @@ export class ReportingTaskService implements PropertyDescriptorRetriever {
createReportingTask(createReportingTask: CreateReportingTaskRequest): Observable<any> {
return this.httpClient.post(`${ReportingTaskService.API}/controller/reporting-tasks`, {
revision: createReportingTask.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
bundle: createReportingTask.reportingTaskBundle,
type: createReportingTask.reportingTaskType
@ -60,8 +63,13 @@ export class ReportingTaskService implements PropertyDescriptorRetriever {
deleteReportingTask(deleteReportingTask: DeleteReportingTaskRequest): Observable<any> {
const entity: ReportingTaskEntity = deleteReportingTask.reportingTask;
const revision: any = this.client.getRevision(entity);
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(entity),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params });
}
startReportingTask(startReportingTask: StartReportingTaskRequest): Observable<any> {
@ -69,6 +77,7 @@ export class ReportingTaskService implements PropertyDescriptorRetriever {
const revision: any = this.client.getRevision(entity);
const payload: any = {
revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'RUNNING'
};
return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, payload);
@ -79,6 +88,7 @@ export class ReportingTaskService implements PropertyDescriptorRetriever {
const revision: any = this.client.getRevision(entity);
const payload: any = {
revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: 'STOPPED'
};
return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, payload);

View File

@ -26,6 +26,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { fullScreenError } from '../../../../state/error/error.actions';
import { ManagementControllerServiceService } from '../management-controller-service.service';
import { selectService } from '../../state/management-controller-services/management-controller-services.selectors';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (
route: ActivatedRouteSnapshot
@ -35,6 +36,7 @@ export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
ManagementControllerServiceService
);
const client: Client = inject(Client);
const clusterConnectionService: ClusterConnectionService = inject(ClusterConnectionService);
// getting id parameter from activated route because ngrx router store
// is not initialized when this resolver executes
@ -74,7 +76,7 @@ export const controllerServiceAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
clientId: revision.clientId,
revision: revision.version,
editable,
disconnectedNodeAcknowledged: false // TODO
disconnectedNodeAcknowledged: clusterConnectionService.isDisconnectionAcknowledged()
} as AdvancedUiParams;
}),
take(1)

View File

@ -26,6 +26,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { fullScreenError } from '../../../../state/error/error.actions';
import { ParameterProviderService } from '../parameter-provider.service';
import { selectParameterProvider } from '../../state/parameter-providers/parameter-providers.selectors';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export const parameterProviderAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (
route: ActivatedRouteSnapshot
@ -33,6 +34,7 @@ export const parameterProviderAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
const store: Store<NiFiState> = inject(Store);
const parameterProviderService: ParameterProviderService = inject(ParameterProviderService);
const client: Client = inject(Client);
const clusterConnectionService: ClusterConnectionService = inject(ClusterConnectionService);
// getting id parameter from activated route because ngrx router store
// is not initialized when this resolver executes
@ -70,7 +72,7 @@ export const parameterProviderAdvancedUiParamsResolver: ResolveFn<AdvancedUiPara
clientId: revision.clientId,
revision: revision.version,
editable: true,
disconnectedNodeAcknowledged: false // TODO
disconnectedNodeAcknowledged: clusterConnectionService.isDisconnectionAcknowledged()
} as AdvancedUiParams;
}),
take(1)

View File

@ -26,11 +26,13 @@ import { HttpErrorResponse } from '@angular/common/http';
import { fullScreenError } from '../../../../state/error/error.actions';
import { ReportingTaskService } from '../reporting-task.service';
import { selectTask } from '../../state/reporting-tasks/reporting-tasks.selectors';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
export const reportingTaskAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams> = (route: ActivatedRouteSnapshot) => {
const store: Store<NiFiState> = inject(Store);
const reportingTaskService: ReportingTaskService = inject(ReportingTaskService);
const client: Client = inject(Client);
const clusterConnectionService: ClusterConnectionService = inject(ClusterConnectionService);
// getting id parameter from activated route because ngrx router store
// is not initialized when this resolver executes
@ -70,7 +72,7 @@ export const reportingTaskAdvancedUiParamsResolver: ResolveFn<AdvancedUiParams>
clientId: revision.clientId,
revision: revision.version,
editable,
disconnectedNodeAcknowledged: false // TODO
disconnectedNodeAcknowledged: clusterConnectionService.isDisconnectionAcknowledged()
} as AdvancedUiParams;
}),
take(1)

View File

@ -33,6 +33,7 @@ export interface Controller {
export interface ControllerEntity {
revision: Revision;
disconnectedNodeAcknowledged?: boolean;
component: Controller;
}

View File

@ -30,6 +30,7 @@ export interface LoadRegistryClientsResponse {
export interface CreateRegistryClientRequest {
revision: Revision;
disconnectedNodeAcknowledged: boolean;
component: {
name: string;
type: string;

View File

@ -24,6 +24,7 @@ import { EditFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/error/error.reducer';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('EditFlowAnalysisRule', () => {
let component: EditFlowAnalysisRule;
@ -106,7 +107,13 @@ describe('EditFlowAnalysisRule', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditFlowAnalysisRule);

View File

@ -46,6 +46,7 @@ import {
} from '../../../state/flow-analysis-rules';
import { FlowAnalysisRuleTable } from '../flow-analysis-rule-table/flow-analysis-rule-table.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-flow-analysis-rule',
@ -91,7 +92,8 @@ export class EditFlowAnalysisRule {
@Inject(MAT_DIALOG_DATA) public request: EditFlowAnalysisRuleDialogRequest,
private formBuilder: FormBuilder,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
const serviceProperties: any = request.flowAnalysisRule.component.properties;
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
@ -124,6 +126,7 @@ export class EditFlowAnalysisRule {
submitForm(postUpdateNavigation?: string[]) {
const payload: any = {
revision: this.client.getRevision(this.request.flowAnalysisRule),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.flowAnalysisRule.id,
name: this.editFlowAnalysisRuleForm.get('name')?.value,

View File

@ -24,6 +24,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('GeneralForm', () => {
let component: GeneralForm;
@ -36,7 +37,13 @@ describe('GeneralForm', () => {
providers: [
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(GeneralForm);

View File

@ -23,6 +23,7 @@ import { updateControllerConfig } from '../../../state/general/general.actions';
import { Client } from '../../../../../service/client.service';
import { selectCurrentUser } from '../../../../../state/current-user/current-user.selectors';
import { selectSaving } from '../../../state/general/general.selectors';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'general-form',
@ -44,6 +45,7 @@ export class GeneralForm {
constructor(
private formBuilder: FormBuilder,
private client: Client,
private clusterConnectionService: ClusterConnectionService,
private store: Store<GeneralState>
) {
// build the form
@ -56,6 +58,7 @@ export class GeneralForm {
const payload: UpdateControllerConfigRequest = {
controller: {
revision: this.client.getRevision(this._controller),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
maxTimerDrivenThreadCount: this.controllerForm.get('timerDrivenThreadCount')?.value
}

View File

@ -43,6 +43,7 @@ import { ParameterProviderReferences } from '../parameter-context-references/par
import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { CommonModule } from '@angular/common';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-parameter-provider',
@ -80,7 +81,8 @@ export class EditParameterProvider {
@Inject(MAT_DIALOG_DATA) public request: EditParameterProviderRequest,
private formBuilder: FormBuilder,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
const providerProperties = request.parameterProvider.component.properties;
const properties: Property[] = Object.entries(providerProperties).map((entry: any) => {
@ -111,6 +113,7 @@ export class EditParameterProvider {
submitForm(postUpdateNavigation?: string[]) {
const payload: any = {
revision: this.client.getRevision(this.request.parameterProvider),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.parameterProvider.id,
name: this.editParameterProviderForm.get('name')?.value,

View File

@ -23,6 +23,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { provideMockStore } from '@ngrx/store/testing';
import { initialParameterProvidersState } from '../../../state/parameter-providers/parameter-providers.reducer';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('FetchParameterProviderParameters', () => {
let component: FetchParameterProviderParameters;
@ -163,7 +164,13 @@ describe('FetchParameterProviderParameters', () => {
},
provideMockStore({
initialState: initialParameterProvidersState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(FetchParameterProviderParameters);

View File

@ -22,7 +22,6 @@ import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators }
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { MatButtonModule } from '@angular/material/button';
import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive';
import { Client } from '../../../../../service/client.service';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
import {
FetchedParameterMapping,
@ -49,6 +48,7 @@ import * as ParameterProviderActions from '../../../state/parameter-providers/pa
import { Store } from '@ngrx/store';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PipesModule } from '../../../../../pipes/pipes.module';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'fetch-parameter-provider-parameters',
@ -107,7 +107,7 @@ export class FetchParameterProviderParameters implements OnInit {
constructor(
private formBuilder: FormBuilder,
private client: Client,
private clusterConnectionService: ClusterConnectionService,
private nifiCommon: NiFiCommon,
private store: Store<ParameterProvidersState>,
@Inject(MAT_DIALOG_DATA) public request: FetchParameterProviderDialogRequest
@ -596,7 +596,7 @@ export class FetchParameterProviderParameters implements OnInit {
return {
id: this.parameterProvider.id,
revision: this.parameterProvider.revision,
disconnectedNodeAcknowledged: false,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
parameterGroupConfigurations: groupConfigs
};
}

View File

@ -21,6 +21,7 @@ import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { CreateRegistryClient } from './create-registry-client.component';
import { CreateRegistryClientDialogRequest } from '../../../state/registry-clients';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('CreateRegistryClient', () => {
let component: CreateRegistryClient;
@ -44,7 +45,15 @@ describe('CreateRegistryClient', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [CreateRegistryClient, NoopAnimationsModule],
providers: [{ provide: MAT_DIALOG_DATA, useValue: data }]
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(CreateRegistryClient);
component = fixture.componentInstance;

View File

@ -31,6 +31,7 @@ import { MatSelectModule } from '@angular/material/select';
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'create-registry-client',
@ -62,7 +63,8 @@ export class CreateRegistryClient {
@Inject(MAT_DIALOG_DATA) public request: CreateRegistryClientDialogRequest,
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
let type: string | null = null;
if (request.registryClientTypes.length > 0) {
@ -94,6 +96,7 @@ export class CreateRegistryClient {
clientId: this.client.getClientId(),
version: 0
},
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
name: this.createRegistryClientForm.get('name')?.value,
type: this.createRegistryClientForm.get('type')?.value,

View File

@ -24,6 +24,7 @@ import { EditRegistryClientDialogRequest } from '../../../state/registry-clients
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/error/error.reducer';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('EditRegistryClient', () => {
let component: EditRegistryClient;
@ -119,7 +120,13 @@ describe('EditRegistryClient', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditRegistryClient);

View File

@ -41,6 +41,7 @@ import { NiFiCommon } from '../../../../../service/nifi-common.service';
import { MatTabsModule } from '@angular/material/tabs';
import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-registry-client',
@ -79,7 +80,8 @@ export class EditRegistryClient {
@Inject(MAT_DIALOG_DATA) public request: EditRegistryClientDialogRequest,
private formBuilder: FormBuilder,
private nifiCommon: NiFiCommon,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {
const serviceProperties: any = request.registryClient.component.properties;
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
@ -113,6 +115,7 @@ export class EditRegistryClient {
submitForm(postUpdateNavigation?: string[]) {
const payload: any = {
revision: this.client.getRevision(this.request.registryClient),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.registryClient.id,
name: this.editRegistryClientForm.get('name')?.value,

View File

@ -24,6 +24,7 @@ import { EditReportingTaskDialogRequest } from '../../../state/reporting-tasks';
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../../state/error/error.reducer';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
describe('EditReportingTask', () => {
let component: EditReportingTask;
@ -399,7 +400,13 @@ describe('EditReportingTask', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditReportingTask);

View File

@ -48,6 +48,7 @@ import { ControllerServiceApi } from '../../../../../ui/common/controller-servic
import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive';
import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component';
import { ErrorBanner } from '../../../../../ui/common/error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../../service/cluster-connection.service';
@Component({
selector: 'edit-reporting-task',
@ -104,7 +105,8 @@ export class EditReportingTask {
@Inject(MAT_DIALOG_DATA) public request: EditReportingTaskDialogRequest,
private formBuilder: FormBuilder,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
const serviceProperties: any = request.reportingTask.component.properties;
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
@ -157,6 +159,7 @@ export class EditReportingTask {
submitForm(postUpdateNavigation?: string[]) {
const payload: any = {
revision: this.client.getRevision(this.request.reportingTask),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.reportingTask.id,
name: this.editReportingTaskForm.get('name')?.value,

View File

@ -20,6 +20,11 @@ import { Store } from '@ngrx/store';
import { NiFiState } from '../../../state';
import { startCurrentUserPolling, stopCurrentUserPolling } from '../../../state/current-user/current-user.actions';
import { resetUsersState } from '../state/user-listing/user-listing.actions';
import {
loadClusterSummary,
startClusterSummaryPolling,
stopClusterSummaryPolling
} from '../../../state/cluster-summary/cluster-summary.actions';
@Component({
selector: 'users',
@ -30,11 +35,14 @@ export class Users implements OnInit, OnDestroy {
constructor(private store: Store<NiFiState>) {}
ngOnInit(): void {
this.store.dispatch(loadClusterSummary());
this.store.dispatch(startCurrentUserPolling());
this.store.dispatch(startClusterSummaryPolling());
}
ngOnDestroy(): void {
this.store.dispatch(resetUsersState());
this.store.dispatch(stopClusterSummaryPolling());
this.store.dispatch(stopCurrentUserPolling());
}
}

View File

@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Client } from '../../../service/client.service';
import { Observable } from 'rxjs';
import { NiFiCommon } from '../../../service/nifi-common.service';
@ -27,6 +27,7 @@ import {
UpdateUserGroupRequest,
UpdateUserRequest
} from '../state/user-listing';
import { ClusterConnectionService } from '../../../service/cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class UsersService {
@ -35,7 +36,8 @@ export class UsersService {
constructor(
private httpClient: HttpClient,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {}
getUsers(): Observable<any> {
@ -49,6 +51,7 @@ export class UsersService {
createUser(request: CreateUserRequest): Observable<any> {
const payload: any = {
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: request.userPayload
};
return this.httpClient.post(`${UsersService.API}/tenants/users`, payload);
@ -57,6 +60,7 @@ export class UsersService {
createUserGroup(request: CreateUserGroupRequest): Observable<any> {
const payload: any = {
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: request.userGroupPayload
};
return this.httpClient.post(`${UsersService.API}/tenants/user-groups`, payload);
@ -65,6 +69,7 @@ export class UsersService {
updateUser(request: UpdateUserRequest): Observable<any> {
const payload: any = {
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: request.id,
...request.userPayload
@ -76,6 +81,7 @@ export class UsersService {
updateUserGroup(request: UpdateUserGroupRequest): Observable<any> {
const payload: any = {
revision: request.revision,
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: request.id,
...request.userGroupPayload
@ -85,12 +91,22 @@ export class UsersService {
}
deleteUser(user: UserEntity): Observable<any> {
const revision: any = this.client.getRevision(user);
return this.httpClient.delete(this.nifiCommon.stripProtocol(user.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(user),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(user.uri), { params });
}
deleteUserGroup(userGroup: UserGroupEntity): Observable<any> {
const revision: any = this.client.getRevision(userGroup);
return this.httpClient.delete(this.nifiCommon.stripProtocol(userGroup.uri), { params: revision });
const params = new HttpParams({
fromObject: {
...this.client.getRevision(userGroup),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
}
});
return this.httpClient.delete(this.nifiCommon.stripProtocol(userGroup.uri), { params });
}
}

View File

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { ClusterSummaryState } from '../state/cluster-summary';
import { selectDisconnectionAcknowledged } from '../state/cluster-summary/cluster-summary.selectors';
@Injectable({
providedIn: 'root'
})
export class ClusterConnectionService {
private disconnectionAcknowledged = this.store.selectSignal(selectDisconnectionAcknowledged);
constructor(private store: Store<ClusterSummaryState>) {}
isDisconnectionAcknowledged(): boolean {
return this.disconnectionAcknowledged();
}
}

View File

@ -25,6 +25,7 @@ import {
} from '../state/shared';
import { Client } from './client.service';
import { NiFiCommon } from './nifi-common.service';
import { ClusterConnectionService } from './cluster-connection.service';
@Injectable({ providedIn: 'root' })
export class ControllerServiceStateService {
@ -33,7 +34,8 @@ export class ControllerServiceStateService {
constructor(
private httpClient: HttpClient,
private nifiCommon: NiFiCommon,
private client: Client
private client: Client,
private clusterConnectionService: ClusterConnectionService
) {}
getControllerService(id: string): Observable<any> {
@ -46,6 +48,7 @@ export class ControllerServiceStateService {
setEnable(controllerService: ControllerServiceEntity, enabled: boolean): Observable<any> {
return this.httpClient.put(`${this.nifiCommon.stripProtocol(controllerService.uri)}/run-status`, {
revision: this.client.getRevision(controllerService),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
state: enabled ? 'ENABLED' : 'DISABLED',
uiOnly: true
});
@ -63,7 +66,7 @@ export class ControllerServiceStateService {
id: controllerService.id,
state: enabled ? 'ENABLED' : 'DISABLED',
referencingComponentRevisions: referencingComponentRevisions,
// 'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
uiOnly: true
});
}
@ -83,7 +86,7 @@ export class ControllerServiceStateService {
id: controllerService.id,
state: running ? 'RUNNING' : 'STOPPED',
referencingComponentRevisions: referencingComponentRevisions,
// 'disconnectedNodeAcknowledged': nfStorage.isDisconnectionAcknowledged(),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
uiOnly: true
});
}

View File

@ -31,6 +31,16 @@ export const loadClusterSummarySuccess = createAction(
props<{ response: LoadClusterSummaryResponse }>()
);
export const acknowledgeClusterConnectionChange = createAction(
`${CLUSTER_SUMMARY_STATE_PREFIX} Acknowledge Cluster Connection Change`,
props<{ connectedToCluster: boolean }>()
);
export const setDisconnectionAcknowledged = createAction(
`${CLUSTER_SUMMARY_STATE_PREFIX} Set Disconnection Acknowledged`,
props<{ disconnectionAcknowledged: boolean }>()
);
export const clusterSummaryApiError = createAction(
`${CLUSTER_SUMMARY_STATE_PREFIX} Cluster Summary Api Error`,
props<{ error: string }>()

View File

@ -18,34 +18,53 @@
import { Injectable } from '@angular/core';
import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects';
import * as ClusterSummaryActions from './cluster-summary.actions';
import { asyncScheduler, catchError, filter, from, interval, map, of, switchMap, takeUntil } from 'rxjs';
import { asyncScheduler, catchError, delay, filter, from, interval, map, of, switchMap, takeUntil, tap } from 'rxjs';
import { ClusterService } from '../../service/cluster.service';
import { selectClusterSummary } from './cluster-summary.selectors';
import { isDefinedAndNotNull } from '../shared';
import { Store } from '@ngrx/store';
import { ClusterSummaryState } from './index';
import { ClusterSummary, ClusterSummaryState } from './index';
import { HttpErrorResponse } from '@angular/common/http';
import * as ErrorActions from '../error/error.actions';
import { acknowledgeClusterConnectionChange, setDisconnectionAcknowledged } from './cluster-summary.actions';
import { OkDialog } from '../../ui/common/ok-dialog/ok-dialog.component';
import { MEDIUM_DIALOG } from '../../index';
import { MatDialog } from '@angular/material/dialog';
@Injectable()
export class ClusterSummaryEffects {
constructor(
private actions$: Actions,
private clusterService: ClusterService,
private store: Store<ClusterSummaryState>
private store: Store<ClusterSummaryState>,
private dialog: MatDialog
) {}
loadClusterSummary$ = createEffect(() =>
this.actions$.pipe(
ofType(ClusterSummaryActions.loadClusterSummary),
switchMap(() => {
concatLatestFrom(() => this.store.select(selectClusterSummary)),
switchMap(([, currentClusterSummary]) => {
return from(
this.clusterService.getClusterSummary().pipe(
map((response) =>
ClusterSummaryActions.loadClusterSummarySuccess({
map((response) => {
const clusterSummary: ClusterSummary = response.clusterSummary;
const connectedToCluster = clusterSummary.connectedToCluster;
if (currentClusterSummary) {
if (currentClusterSummary.connectedToCluster !== clusterSummary.connectedToCluster) {
this.store.dispatch(acknowledgeClusterConnectionChange({ connectedToCluster }));
}
} else {
if (clusterSummary.clustered && !clusterSummary.connectedToCluster) {
this.store.dispatch(acknowledgeClusterConnectionChange({ connectedToCluster }));
}
}
return ClusterSummaryActions.loadClusterSummarySuccess({
response
})
),
});
}),
catchError((error) => of(ClusterSummaryActions.clusterSummaryApiError({ error: error.error })))
)
);
@ -65,6 +84,41 @@ export class ClusterSummaryEffects {
)
);
acknowledgeClusterConnectionChange$ = createEffect(
() =>
this.actions$.pipe(
ofType(ClusterSummaryActions.acknowledgeClusterConnectionChange),
delay(2000), // minor delay to allow component at the desired route to act first
tap(({ connectedToCluster }) => {
const message = connectedToCluster
? 'This node just joined the cluster. Any modifications to the data flow made here will replicate across the cluster.'
: 'This node is currently not connected to the cluster. Any modifications to the data flow made here will not replicate across the cluster.';
const dialogReference = this.dialog.open(OkDialog, {
...MEDIUM_DIALOG,
disableClose: true,
data: {
title: 'Cluster Connection',
message
}
});
dialogReference.componentInstance.ok
.pipe(takeUntil(dialogReference.afterClosed()))
.subscribe(() => {
if (connectedToCluster) {
// this node has rejoined the cluster and any previous acknowledged disconnection can be reset
this.store.dispatch(setDisconnectionAcknowledged({ disconnectionAcknowledged: false }));
} else {
// this node is not currently connected so the user has acknowledged the disconnection
this.store.dispatch(setDisconnectionAcknowledged({ disconnectionAcknowledged: true }));
}
});
})
),
{ dispatch: false }
);
searchCluster$ = createEffect(() =>
this.actions$.pipe(
ofType(ClusterSummaryActions.searchCluster),

View File

@ -22,10 +22,12 @@ import {
clusterSummaryApiError,
loadClusterSummary,
loadClusterSummarySuccess,
searchClusterSuccess
searchClusterSuccess,
setDisconnectionAcknowledged
} from './cluster-summary.actions';
export const initialState: ClusterSummaryState = {
disconnectionAcknowledged: false,
clusterSummary: null,
searchResults: null,
error: null,
@ -57,5 +59,9 @@ export const clusterSummaryReducer = createReducer(
on(searchClusterSuccess, (state, { response }) => ({
...state,
searchResults: response
})),
on(setDisconnectionAcknowledged, (state, { disconnectionAcknowledged }) => ({
...state,
disconnectionAcknowledged
}))
);

View File

@ -20,6 +20,11 @@ import { clusterSummaryFeatureKey, ClusterSummaryState } from './index';
export const selectClusterSummaryState = createFeatureSelector<ClusterSummaryState>(clusterSummaryFeatureKey);
export const selectDisconnectionAcknowledged = createSelector(
selectClusterSummaryState,
(state: ClusterSummaryState) => state.disconnectionAcknowledged
);
export const selectClusterSummary = createSelector(
selectClusterSummaryState,
(state: ClusterSummaryState) => state.clusterSummary

View File

@ -34,17 +34,18 @@ export interface NodeSearchResult {
address: string;
}
export interface ClusterSearchRequest {
q?: string;
}
export interface ClusterSearchResults {
nodeResults: NodeSearchResult[];
}
export interface ClusterSummaryState {
disconnectionAcknowledged: boolean;
clusterSummary: ClusterSummary | null;
searchResults: ClusterSearchResults | null;
error: string | null;
status: 'pending' | 'loading' | 'error' | 'success';
}
export interface ClusterSearchRequest {
q?: string;
}

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, SecurityContext } from '@angular/core';
import { Component, OnDestroy, OnInit, SecurityContext } from '@angular/core';
import { NiFiState } from '../../../state';
import { Store } from '@ngrx/store';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
@ -24,6 +24,12 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { Navigation } from '../navigation/navigation.component';
import { selectRouteData } from '../../../state/router/router.selectors';
import { AdvancedUiParams, isDefinedAndNotNull } from '../../../state/shared';
import {
loadClusterSummary,
startClusterSummaryPolling,
stopClusterSummaryPolling
} from '../../../state/cluster-summary/cluster-summary.actions';
import { selectDisconnectionAcknowledged } from '../../../state/cluster-summary/cluster-summary.selectors';
@Component({
selector: 'advanced-ui',
@ -32,9 +38,11 @@ import { AdvancedUiParams, isDefinedAndNotNull } from '../../../state/shared';
imports: [Navigation],
styleUrls: ['./advanced-ui.component.scss']
})
export class AdvancedUi {
export class AdvancedUi implements OnInit, OnDestroy {
frameSource!: SafeResourceUrl | null;
private params: AdvancedUiParams | null = null;
constructor(
private store: Store<NiFiState>,
private domSanitizer: DomSanitizer
@ -44,9 +52,38 @@ export class AdvancedUi {
.pipe(takeUntilDestroyed(), isDefinedAndNotNull())
.subscribe((data) => {
if (data['advancedUiParams']) {
// clone the params to handle reloading based on cluster connection state changes
this.params = {
...data['advancedUiParams']
};
this.frameSource = this.getFrameSource(data['advancedUiParams']);
}
});
this.store
.select(selectDisconnectionAcknowledged)
.pipe(takeUntilDestroyed())
.subscribe((disconnectionAcknowledged) => {
if (this.params) {
// limit reloading advanced ui to only when necessary (when the user has acknowledged disconnection)
if (
disconnectionAcknowledged &&
this.params.disconnectedNodeAcknowledged != disconnectionAcknowledged
) {
this.params.disconnectedNodeAcknowledged = disconnectionAcknowledged;
this.frameSource = this.getFrameSource(this.params);
}
}
});
}
ngOnInit(): void {
this.store.dispatch(loadClusterSummary());
this.store.dispatch(startClusterSummaryPolling());
}
ngOnDestroy(): void {
this.store.dispatch(stopClusterSummaryPolling());
}
private getFrameSource(params: AdvancedUiParams): SafeResourceUrl | null {

View File

@ -24,6 +24,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Component } from '@angular/core';
import { provideMockStore } from '@ngrx/store/testing';
import { initialState } from '../../../../state/error/error.reducer';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
describe('EditControllerService', () => {
let component: EditControllerService;
@ -558,7 +559,13 @@ describe('EditControllerService', () => {
{ provide: MAT_DIALOG_DATA, useValue: data },
provideMockStore({
initialState
})
}),
{
provide: ClusterConnectionService,
useValue: {
isDisconnectionAcknowledged: jest.fn()
}
}
]
});
fixture = TestBed.createComponent(EditControllerService);

View File

@ -44,6 +44,7 @@ import { Observable } from 'rxjs';
import { ControllerServiceReferences } from '../controller-service-references/controller-service-references.component';
import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
import { ErrorBanner } from '../../error-banner/error-banner.component';
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
@Component({
selector: 'edit-controller-service',
@ -109,7 +110,8 @@ export class EditControllerService {
@Inject(MAT_DIALOG_DATA) public request: EditControllerServiceDialogRequest,
private formBuilder: FormBuilder,
private client: Client,
private nifiCommon: NiFiCommon
private nifiCommon: NiFiCommon,
private clusterConnectionService: ClusterConnectionService
) {
const serviceProperties: any = request.controllerService.component.properties;
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
@ -141,6 +143,7 @@ export class EditControllerService {
submitForm(postUpdateNavigation?: string[]) {
const payload: any = {
revision: this.client.getRevision(this.request.controllerService),
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
component: {
id: this.request.controllerService.id,
name: this.editControllerServiceForm.get('name')?.value,

View File

@ -20,5 +20,5 @@
<div class="text-sm">{{ request.message }}</div>
</mat-dialog-content>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close cdkFocusInitial>Ok</button>
<button mat-button mat-dialog-close cdkFocusInitial (click)="okClicked()">Ok</button>
</mat-dialog-actions>

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Inject } from '@angular/core';
import { Component, EventEmitter, Inject, Output } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { OkDialogRequest } from '../../../state/shared';
import { MatButtonModule } from '@angular/material/button';
@ -28,5 +28,11 @@ import { MatButtonModule } from '@angular/material/button';
styleUrls: ['./ok-dialog.component.scss']
})
export class OkDialog {
@Output() ok: EventEmitter<void> = new EventEmitter<void>();
constructor(@Inject(MAT_DIALOG_DATA) public request: OkDialogRequest) {}
okClicked(): void {
this.ok.next();
}
}