diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.actions.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.actions.ts index dec106d2f812..b7bdfbaa66f2 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.actions.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.actions.ts @@ -90,6 +90,7 @@ import { StartComponentRequest, StartComponentResponse, StartComponentsRequest, + StartPollingProcessorUntilStoppedRequest, StartProcessGroupRequest, StartProcessGroupResponse, StopComponentRequest, @@ -778,6 +779,20 @@ export const pollChangeVersionSuccess = createAction( export const stopPollingChangeVersion = createAction(`${CANVAS_PREFIX} Stop Polling Change Version`); +export const startPollingProcessorUntilStopped = createAction( + `${CANVAS_PREFIX} Start Polling Processor Until Stopped`, + props<{ request: StartPollingProcessorUntilStoppedRequest }>() +); + +export const pollProcessorUntilStopped = createAction(`${CANVAS_PREFIX} Poll Processor Until Stopped`); + +export const pollProcessorUntilStoppedSuccess = createAction( + `${CANVAS_PREFIX} Poll Processor Until Stopped Success`, + props<{ response: LoadProcessorSuccess }>() +); + +export const stopPollingProcessor = createAction(`${CANVAS_PREFIX} Stop Polling Processor`); + export const openSaveVersionDialog = createAction( `${CANVAS_PREFIX} Open Save Flow Version Dialog`, props<{ request: SaveVersionDialogRequest }>() diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts index ffc14fa94471..353d5bbe6fe3 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts @@ -53,6 +53,8 @@ import { SaveVersionRequest, SelectedComponent, Snippet, + StartComponentRequest, + StopComponentRequest, StopVersionControlRequest, StopVersionControlResponse, UpdateComponentFailure, @@ -77,6 +79,7 @@ import { selectParentProcessGroupId, selectProcessGroup, selectProcessor, + selectPollingProcessor, selectRefreshRpgDetails, selectRemoteProcessGroup, selectSaving, @@ -158,6 +161,7 @@ import { selectDocumentVisibilityState } from '../../../../state/document-visibi import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { DocumentVisibility } from '../../../../state/document-visibility'; import { ErrorContextKey } from '../../../../state/error'; +import { startComponent, startPollingProcessorUntilStopped, stopComponent } from './flow.actions'; @Injectable() export class FlowEffects { @@ -1550,6 +1554,83 @@ export class FlowEffects { ); }); + this.store + .select(selectProcessor(processorId)) + .pipe( + takeUntil(editDialogReference.afterClosed()), + isDefinedAndNotNull(), + filter((processorEntity) => { + return ( + processorEntity.revision.clientId === this.client.getClientId() || + processorEntity.revision.clientId === request.entity.revision.clientId + ); + }) + ) + .subscribe((response) => { + editDialogReference.componentInstance.processorUpdates = response; + + if ( + !editDialogReference.componentInstance.isStoppable(response) && + !editDialogReference.componentInstance.isRunnable(response) + ) { + this.store.dispatch( + startPollingProcessorUntilStopped({ + request: { + id: response.id, + uri: response.uri, + type: ComponentType.Processor, + revision: response.revision, + errorStrategy: 'snackbar' + } + }) + ); + } + }); + + editDialogReference.componentInstance.stopComponentRequest + .pipe(takeUntil(editDialogReference.afterClosed())) + .subscribe((stopComponentRequest: StopComponentRequest) => { + this.store.dispatch( + stopComponent({ + request: { + id: stopComponentRequest.id, + uri: stopComponentRequest.uri, + type: ComponentType.Processor, + revision: stopComponentRequest.revision, + errorStrategy: 'snackbar' + } + }) + ); + + this.store.dispatch( + startPollingProcessorUntilStopped({ + request: { + id: stopComponentRequest.id, + uri: stopComponentRequest.uri, + type: ComponentType.Processor, + revision: stopComponentRequest.revision, + errorStrategy: 'snackbar' + } + }) + ); + }); + + editDialogReference.componentInstance.startComponentRequest + .pipe(takeUntil(editDialogReference.afterClosed())) + .subscribe((startComponentRequest: StartComponentRequest) => { + this.store.dispatch( + startComponent({ + request: { + id: startComponentRequest.id, + uri: startComponentRequest.uri, + type: ComponentType.Processor, + revision: startComponentRequest.revision, + errorStrategy: 'snackbar' + } + }) + ); + }); + editDialogReference.afterClosed().subscribe((response) => { this.store.dispatch(resetPropertyVerificationState()); if (response != 'ROUTED') { @@ -1572,6 +1653,57 @@ export class FlowEffects { { dispatch: false } ); + startPollingProcessorUntilStopped = createEffect(() => + this.actions$.pipe( + ofType(FlowActions.startPollingProcessorUntilStopped), + switchMap(() => + interval(2000, asyncScheduler).pipe( + takeUntil(this.actions$.pipe(ofType(FlowActions.stopPollingProcessor))) + ) + ), + switchMap(() => of(FlowActions.pollProcessorUntilStopped())) + ) + ); + + pollProcessorUntilStopped$ = createEffect(() => + this.actions$.pipe( + ofType(FlowActions.pollProcessorUntilStopped), + concatLatestFrom(() => [this.store.select(selectPollingProcessor).pipe(isDefinedAndNotNull())]), + switchMap(([, pollingProcessor]) => { + return from( + this.flowService.getProcessor(pollingProcessor.id).pipe( + map((response) => + FlowActions.pollProcessorUntilStoppedSuccess({ + response: { + id: pollingProcessor.id, + processor: response + } + }) + ), + catchError((errorResponse: HttpErrorResponse) => { + this.store.dispatch(FlowActions.stopPollingProcessor()); + return of(this.snackBarOrFullScreenError(errorResponse)); + }) + ) + ); + }) + ) + ); + + pollProcessorUntilStoppedSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(FlowActions.pollProcessorUntilStoppedSuccess), + map((action) => action.response), + filter((response) => { + return !( + response.processor.status.runStatus === 'Stopped' && + response.processor.status.aggregateSnapshot.activeThreadCount > 0 + ); + }), + switchMap(() => of(FlowActions.stopPollingProcessor())) + ) + ); + openEditConnectionDialog$ = createEffect( () => this.actions$.pipe( diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts index d284d643df0e..c44cadef4aad 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts @@ -51,6 +51,7 @@ import { navigateWithoutTransform, pasteSuccess, pollChangeVersionSuccess, + pollProcessorUntilStoppedSuccess, pollRevertChangesSuccess, requestRefreshRemoteProcessGroup, resetFlowState, @@ -69,10 +70,12 @@ import { setTransitionRequired, startComponent, startComponentSuccess, + startPollingProcessorUntilStopped, startProcessGroupSuccess, startRemoteProcessGroupPolling, stopComponent, stopComponentSuccess, + stopPollingProcessor, stopProcessGroupSuccess, stopRemoteProcessGroupPolling, stopVersionControl, @@ -93,6 +96,7 @@ import { produce } from 'immer'; export const initialState: FlowState = { id: 'root', changeVersionRequest: null, + pollingProcessor: null, flow: { permissions: { canRead: false, @@ -296,7 +300,7 @@ export const flowReducer = createReducer( } }); }), - on(loadProcessorSuccess, (state, { response }) => { + on(loadProcessorSuccess, pollProcessorUntilStoppedSuccess, (state, { response }) => { return produce(state, (draftState) => { const proposedProcessor = response.processor; const componentIndex: number = draftState.flow.processGroupFlow.flow.processors.findIndex( @@ -372,6 +376,14 @@ export const flowReducer = createReducer( saving: false, versionSaving: false })), + on(startPollingProcessorUntilStopped, (state, { request }) => ({ + ...state, + pollingProcessor: request + })), + on(stopPollingProcessor, (state) => ({ + ...state, + pollingProcessor: null + })), on( createProcessor, createProcessGroup, diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts index fb4620432ea9..927c0b5f552a 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/flow.selectors.ts @@ -30,6 +30,8 @@ export const selectChangeVersionRequest = createSelector( (state: FlowState) => state.changeVersionRequest ); +export const selectPollingProcessor = createSelector(selectFlowState, (state: FlowState) => state.pollingProcessor); + export const selectSaving = createSelector(selectFlowState, (state: FlowState) => state.saving); export const selectVersionSaving = createSelector(selectFlowState, (state: FlowState) => state.versionSaving); diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/index.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/index.ts index d1d9041e6dbe..0dd834db6236 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/index.ts +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/flow/index.ts @@ -642,6 +642,7 @@ export interface FlowState { versionSaving: boolean; changeVersionRequest: FlowUpdateRequestEntity | null; copiedSnippet: CopiedSnippet | null; + pollingProcessor: any | null; status: 'pending' | 'loading' | 'success' | 'complete'; } @@ -775,6 +776,14 @@ export interface StopComponentRequest { errorStrategy: 'snackbar' | 'banner'; } +export interface StartPollingProcessorUntilStoppedRequest { + id: string; + uri: string; + type: ComponentType; + revision: Revision; + errorStrategy: 'snackbar' | 'banner'; +} + export interface StopProcessGroupRequest { id: string; type: ComponentType; diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html index 93a9b035fe30..9d8fcdf5e5ac 100644 --- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html +++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.html @@ -17,17 +17,51 @@