mcgilman commented on code in PR #8965:
URL: https://github.com/apache/nifi/pull/8965#discussion_r1642840436


##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts:
##########
@@ -635,6 +636,82 @@ export class ControllerServicesEffects {
         { dispatch: false }
     );
 
+    openMoveControllerServiceDialog$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                
ofType(ControllerServicesActions.openMoveControllerServiceDialog),
+                map((action) => action.request),
+                concatLatestFrom(() => 
this.store.select(selectCurrentProcessGroupId)),
+                tap(([request, currentProcessGroupId]) => {
+                    const serviceId: string = request.id;
+                    request.processGroupFlow = 
this.flowService.getFlow(currentProcessGroupId);
+                    const moveDialogReference = 
this.dialog.open(MoveControllerService, {
+                        ...XL_DIALOG,
+                        data: request,
+                        id: serviceId
+                    });
+
+                    
moveDialogReference.componentInstance.goToReferencingComponent = (
+                        component: ControllerServiceReferencingComponent
+                    ) => {
+                        const route: string[] = 
this.getRouteForReference(component);
+                        this.router.navigate(route);
+                    };
+
+                    moveDialogReference.afterClosed().subscribe((response) => {
+                        if (response != 'ROUTED') {
+                            this.store.dispatch(
+                                
ControllerServicesActions.loadControllerServices({
+                                    request: {
+                                        processGroupId: currentProcessGroupId
+                                    }
+                                })
+                            );
+                        }
+                    });
+                })
+            ),
+        { dispatch: false }
+    );
+
+    moveControllerService$ = createEffect(() =>
+        this.actions$.pipe(
+            ofType(ControllerServicesActions.moveControllerService),
+            map((action) => action.request),
+            switchMap((request) =>
+                
from(this.controllerServiceService.moveControllerService(request)).pipe(
+                    map((response) =>
+                        
ControllerServicesActions.moveControllerServiceSuccess({
+                            response: {
+                                controllerService: response
+                            }
+                        })
+                    ),
+                    catchError((errorResponse: HttpErrorResponse) =>
+                        of(ErrorActions.snackBarError({ error: 
this.errorHelper.getErrorString(errorResponse) }))
+                    )
+                )
+            )
+        )
+    );
+
+    moveControllerServiceSuccess$ = createEffect( () =>
+        this.actions$.pipe(
+            ofType(ControllerServicesActions.moveControllerServiceSuccess),
+            map((action) => action.response),
+            tap((request) => {
+                this.dialog.closeAll();
+                this.router.navigate([
+                    '/process-groups',
+                    request.controllerService.parentGroupId,
+                    'controller-services',
+                    request.controllerService.id
+                ]);

Review Comment:
   Do we want to navigate to the service that was moved here? On the canvas 
when we move a component to a child or parent group the user is not navigated 
automatically. Similar if someone moves a file into a folder in there OS.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts:
##########
@@ -635,6 +636,82 @@ export class ControllerServicesEffects {
         { dispatch: false }
     );
 
+    openMoveControllerServiceDialog$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                
ofType(ControllerServicesActions.openMoveControllerServiceDialog),
+                map((action) => action.request),
+                concatLatestFrom(() => 
this.store.select(selectCurrentProcessGroupId)),
+                tap(([request, currentProcessGroupId]) => {
+                    const serviceId: string = request.id;
+                    request.processGroupFlow = 
this.flowService.getFlow(currentProcessGroupId);
+                    const moveDialogReference = 
this.dialog.open(MoveControllerService, {
+                        ...XL_DIALOG,

Review Comment:
   Is an XL Dialog needed here? It seems like the a smaller dialog may be more 
appropriate.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);
+
+    moveControllerServiceForm: FormGroup;
+
+    @ViewChild('stepComplete') stepComplete!: TemplateRef<any>;
+    @ViewChild('stepError') stepError!: TemplateRef<any>;
+    @ViewChild('stepInProgress') stepInProgress!: TemplateRef<any>;
+    @ViewChild('stepNotStarted') stepNotStarted!: TemplateRef<any>;
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public request: 
MoveControllerServiceDialogRequest,
+        private store: Store<ControllerServiceState>,
+        private formBuilder: FormBuilder
+    ) {
+        super();
+
+        request.processGroupFlow.subscribe((flow:any) => {
+            let processGroups: SelectOption[] = [];
+            let processGroupflow = flow.processGroupFlow;
+            if 
(processGroupflow.breadcrumb.hasOwnProperty('parentBreadcrumb')) {
+                let parentBreadcrumb = 
processGroupflow.breadcrumb.parentBreadcrumb;
+                if (parentBreadcrumb.permissions.canRead && 
parentBreadcrumb.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: parentBreadcrumb.breadcrumb.name + ' (Parent)',
+                        value: parentBreadcrumb.breadcrumb.id
+                    }
+                    processGroups.push(option);
+                }
+            }
+
+            processGroupflow.flow.processGroups.forEach((child:any) => {
+                if (child.permissions.canRead && child.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: child.component.name,
+                        value: child.component.id
+                    }
+                    processGroups.push(option);
+                }
+            });

Review Comment:
   We should only allow the user to choose a child component if the reduction 
in Controller Service scope will be allowed. By moving to a child Group if 
there are any components referencing that Controller Service in the current 
Group won't be able to.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts:
##########
@@ -132,6 +132,12 @@ export interface EditControllerServiceDialogRequest {
     history?: ComponentHistory;
 }
 
+export interface MoveControllerServiceDialogRequest {
+    id: string;
+    controllerService: ControllerServiceEntity;
+    processGroupFlow: any;
+}

Review Comment:
   Like the new `Move` component, I think this dialog request should be moved 
into the `flow-designer` page. It's not applicable outside of that page.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';

Review Comment:
   This component shouldn't be referencing `ControllerServiceState`.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts:
##########
@@ -635,6 +636,82 @@ export class ControllerServicesEffects {
         { dispatch: false }
     );
 
+    openMoveControllerServiceDialog$ = createEffect(
+        () =>
+            this.actions$.pipe(
+                
ofType(ControllerServicesActions.openMoveControllerServiceDialog),
+                map((action) => action.request),
+                concatLatestFrom(() => 
this.store.select(selectCurrentProcessGroupId)),
+                tap(([request, currentProcessGroupId]) => {
+                    const serviceId: string = request.id;
+                    request.processGroupFlow = 
this.flowService.getFlow(currentProcessGroupId);

Review Comment:
   The state for the `flow-designer` page is already loaded with the current 
process group. We should be able to simply use that instead of making another 
request here.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts:
##########
@@ -132,6 +132,12 @@ export interface EditControllerServiceDialogRequest {
     history?: ComponentHistory;
 }
 
+export interface MoveControllerServiceDialogRequest {
+    id: string;
+    controllerService: ControllerServiceEntity;
+    processGroupFlow: any;
+}

Review Comment:
   `processGroupFlow` shouldn't need to be `any` for this dialog request. 
Admittedly there is some instances of `any` based on the underlying data model 
the back end uses but in this case there already is a defined type for this 
field that should be referenced.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.reducer.ts:
##########
@@ -99,6 +100,15 @@ export const controllerServicesReducer = createReducer(
             draftState.saving = false;
         });
     }),
+    on(moveControllerServiceSuccess, (state, { response }) => {
+        return produce(state, (draftState) => {
+            const componentIndex: number = 
draftState.controllerServices.findIndex((f: any) => 
response.controllerService.id === f.id);
+            if (componentIndex > -1) {
+                draftState.controllerServices[componentIndex] = 
response.controllerService;
+            }
+            draftState.saving = false;
+        });
+    }),

Review Comment:
   When this reducer runs the store contains the services for the original 
Parent Group (before the move is executed). I think that this reducer should be 
removing the current service rather than updating it since the service no 
longer belongs to the original Parent Group.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,

Review Comment:
   This component seems to be storing the current Controller Service in the 
store for Enable/Disable Service. Since this action is only applicable to 
services in the Flow, it should be using that store. Enable/Disable has it's 
own store so it can be used by both Service listings.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html:
##########
@@ -173,6 +173,12 @@
                                         Manage Access Policies
                                     </button>
                                 }
+                                @if (canConfigure(item)) {
+                                    <button mat-menu-item 
(click)="moveClicked(item)">
+                                        <i class="fa fa-arrows primary-color 
mr-2"></i>
+                                        Move
+                                    </button>
+                                }

Review Comment:
   This table is used for both services in the Flow as well as services in the 
Controller Settings. This is why the `ControllerServiceTable` is in the 
`common` components. Since Services in the Controller Settings do not exist 
within a Process Group, this action is not be available.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {

Review Comment:
   Since this component only is applicable to the Service listing in the Flow, 
I think it makes sense to move this component out of `common` and into 
`flow-designer`. Components in `common` should be independent of a given page 
and this component currently references a number of things from the 
`flow-designer` page. 



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);
+
+    moveControllerServiceForm: FormGroup;
+
+    @ViewChild('stepComplete') stepComplete!: TemplateRef<any>;
+    @ViewChild('stepError') stepError!: TemplateRef<any>;
+    @ViewChild('stepInProgress') stepInProgress!: TemplateRef<any>;
+    @ViewChild('stepNotStarted') stepNotStarted!: TemplateRef<any>;

Review Comment:
   These appear to be unused.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.html:
##########
@@ -0,0 +1,82 @@
+<!--
+  ~ 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.
+  -->
+
+<h2 mat-dialog-title>Move Controller Service</h2>
+@if ((controllerService$ | async)!; as controllerService) {
+<form class="controller-service-enable-form" 
[formGroup]="moveControllerServiceForm">
+    <mat-dialog-content>
+        <div class="py-4 flex gap-x-4">
+            <div class="flex basis-2/3 flex-col gap-y-4 pr-4 overflow-hidden">
+                <div class="flex flex-col">
+                    <div>Service</div>
+                    <div
+                        class="accent-color font-medium overflow-ellipsis 
overflow-hidden whitespace-nowrap"
+                        [title]="controllerService.component.name">
+                        {{ controllerService.component.name }}
+                    </div>
+                </div>
+                <div>
+                    <mat-form-field>
+                        <mat-label>Process Group:</mat-label>
+                        <mat-select formControlName="processGroups" >
+                            @for (option of 
controllerServiceActionProcessGroups; track option) {
+                            <mat-option
+                                [value]="option.value">
+                                {{ option.text }}
+                            </mat-option>
+                            }
+                        </mat-select>
+                    </mat-form-field>
+                </div>
+            </div>
+            <div class="flex basis-1/3 flex-col">
+                <div>
+                    Referencing Components
+                    <i
+                        class="fa fa-info-circle"
+                        nifiTooltip
+                        [tooltipComponentType]="TextTip"
+                        tooltipInputData="Other components referencing this 
controller service."></i>
+                </div>
+                <div class="relative h-full border" style="min-height: 320px">
+                    <div class="absolute inset-0 overflow-y-auto p-1">
+                        <controller-service-references
+                            
[serviceReferences]="controllerService.component.referencingComponents"
+                            [goToReferencingComponent]="
+                                            goToReferencingComponent
+                                        "></controller-service-references>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </mat-dialog-content>
+    <mat-dialog-actions align="end">
+        <button mat-button mat-dialog-close>Cancel</button>
+        <button type="button" color="primary" (click)="submitForm()" 
mat-button>Move</button>
+    </mat-dialog-actions>
+</form>
+}
+<ng-template #stepInProgress>
+    <div class="fa fa-spin fa-circle-o-notch primary-color"></div>
+</ng-template>
+<ng-template #stepComplete>
+    <div class="complete fa fa-check success-color"></div>
+</ng-template>
+<ng-template #stepError>
+    <div class="fa fa-times warn-color"></div>
+</ng-template>
+<ng-template #stepNotStarted><div class="w-3.5"></div></ng-template>

Review Comment:
   These appear to be unused.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);
+
+    moveControllerServiceForm: FormGroup;
+
+    @ViewChild('stepComplete') stepComplete!: TemplateRef<any>;
+    @ViewChild('stepError') stepError!: TemplateRef<any>;
+    @ViewChild('stepInProgress') stepInProgress!: TemplateRef<any>;
+    @ViewChild('stepNotStarted') stepNotStarted!: TemplateRef<any>;
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public request: 
MoveControllerServiceDialogRequest,
+        private store: Store<ControllerServiceState>,
+        private formBuilder: FormBuilder
+    ) {
+        super();
+
+        request.processGroupFlow.subscribe((flow:any) => {
+            let processGroups: SelectOption[] = [];
+            let processGroupflow = flow.processGroupFlow;
+            if 
(processGroupflow.breadcrumb.hasOwnProperty('parentBreadcrumb')) {
+                let parentBreadcrumb = 
processGroupflow.breadcrumb.parentBreadcrumb;
+                if (parentBreadcrumb.permissions.canRead && 
parentBreadcrumb.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: parentBreadcrumb.breadcrumb.name + ' (Parent)',
+                        value: parentBreadcrumb.breadcrumb.id
+                    }
+                    processGroups.push(option);
+                }
+            }
+
+            processGroupflow.flow.processGroups.forEach((child:any) => {
+                if (child.permissions.canRead && child.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: child.component.name,
+                        value: child.component.id
+                    }
+                    processGroups.push(option);
+                }
+            });
+
+            this.controllerServiceActionProcessGroups = processGroups;
+            if (processGroups.length > 0) {
+                
this.moveControllerServiceForm.controls['processGroups'].setValue(processGroups[0].value);
+            }
+        });
+
+        // build the form
+        this.moveControllerServiceForm = this.formBuilder.group({
+            processGroups: new FormControl("Process Group", 
Validators.required)
+        });
+
+        this.store.dispatch(
+            setControllerService({
+                request: {
+                    controllerService: request.controllerService
+                }
+            })
+        );
+
+    }
+
+    submitForm() {
+        this.store.dispatch(
+            moveControllerService({
+                request: {
+                    controllerService: this.request.controllerService,
+                    data: {
+                        parentGroupId: 
this.moveControllerServiceForm.get('processGroups')?.value,
+                        revision: this.request.controllerService.revision
+                    }
+                }
+            })
+        );
+
+    }
+
+    cancelClicked(): void {
+        this.store.dispatch(stopPollingControllerService());
+    }

Review Comment:
   This appears to be unused.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);
+
+    moveControllerServiceForm: FormGroup;
+
+    @ViewChild('stepComplete') stepComplete!: TemplateRef<any>;
+    @ViewChild('stepError') stepError!: TemplateRef<any>;
+    @ViewChild('stepInProgress') stepInProgress!: TemplateRef<any>;
+    @ViewChild('stepNotStarted') stepNotStarted!: TemplateRef<any>;
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public request: 
MoveControllerServiceDialogRequest,
+        private store: Store<ControllerServiceState>,

Review Comment:
   ```suggestion
           private store: Store<NiFiState>,
   ```



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);

Review Comment:
   I don't think this component needs to select anything from a store. The 
Controller Service in question is supplied by the `request` constructor 
argument.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.ts:
##########
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Component, Inject, Input, TemplateRef, ViewChild } from 
'@angular/core';
+import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
+import { FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators 
} from '@angular/forms';
+import {
+    ControllerServiceReferencingComponent,
+    MoveControllerServiceDialogRequest,
+} from '../../../../state/shared';
+import { MatInputModule } from '@angular/material/input';
+import { MatCheckboxModule } from '@angular/material/checkbox';
+import { MatButtonModule } from '@angular/material/button';
+import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { MatTabsModule } from '@angular/material/tabs';
+import { MatOptionModule } from '@angular/material/core';
+import { MatSelectModule } from '@angular/material/select';
+import { ControllerServiceApi } from 
'../controller-service-api/controller-service-api.component';
+import { ControllerServiceReferences } from 
'../controller-service-references/controller-service-references.component';
+import { NifiSpinnerDirective } from '../../spinner/nifi-spinner.directive';
+import { TextTip } from '../../tooltips/text-tip/text-tip.component';
+import { NifiTooltipDirective } from '../../tooltips/nifi-tooltip.directive';
+import { ControllerServiceState } from 
'../../../../state/contoller-service-state';
+import { Store } from '@ngrx/store';
+import {
+    setControllerService,
+    stopPollingControllerService,
+} from 
'../../../../state/contoller-service-state/controller-service-state.actions';
+import { selectControllerService } from 
'../../../../state/contoller-service-state/controller-service-state.selectors';
+import { CloseOnEscapeDialog } from 
'../../close-on-escape-dialog/close-on-escape-dialog.component';
+import { moveControllerService } from 
'./../../../../pages/flow-designer/state/controller-services/controller-services.actions';
+import { SelectOption } from './../../../../state/shared/index';
+
+@Component({
+    selector: 'move-controller-service',
+    standalone: true,
+    templateUrl: './move-controller-service.component.html',
+    imports: [
+        ReactiveFormsModule,
+        MatDialogModule,
+        MatInputModule,
+        MatCheckboxModule,
+        MatButtonModule,
+        MatTabsModule,
+        MatOptionModule,
+        MatSelectModule,
+        ControllerServiceApi,
+        ControllerServiceReferences,
+        AsyncPipe,
+        NifiSpinnerDirective,
+        NifiTooltipDirective,
+        NgTemplateOutlet
+    ],
+    styleUrls: ['./move-controller-service.component.scss']
+})
+export class MoveControllerService extends CloseOnEscapeDialog {
+    @Input() goToReferencingComponent!: (component: 
ControllerServiceReferencingComponent) => void;
+    protected readonly TextTip = TextTip;
+    protected controllerServiceActionProcessGroups: SelectOption[] = [];
+
+    controllerService$ = this.store.select(selectControllerService);
+
+    moveControllerServiceForm: FormGroup;
+
+    @ViewChild('stepComplete') stepComplete!: TemplateRef<any>;
+    @ViewChild('stepError') stepError!: TemplateRef<any>;
+    @ViewChild('stepInProgress') stepInProgress!: TemplateRef<any>;
+    @ViewChild('stepNotStarted') stepNotStarted!: TemplateRef<any>;
+
+    constructor(
+        @Inject(MAT_DIALOG_DATA) public request: 
MoveControllerServiceDialogRequest,
+        private store: Store<ControllerServiceState>,
+        private formBuilder: FormBuilder
+    ) {
+        super();
+
+        request.processGroupFlow.subscribe((flow:any) => {
+            let processGroups: SelectOption[] = [];
+            let processGroupflow = flow.processGroupFlow;
+            if 
(processGroupflow.breadcrumb.hasOwnProperty('parentBreadcrumb')) {
+                let parentBreadcrumb = 
processGroupflow.breadcrumb.parentBreadcrumb;
+                if (parentBreadcrumb.permissions.canRead && 
parentBreadcrumb.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: parentBreadcrumb.breadcrumb.name + ' (Parent)',
+                        value: parentBreadcrumb.breadcrumb.id
+                    }
+                    processGroups.push(option);
+                }
+            }
+
+            processGroupflow.flow.processGroups.forEach((child:any) => {
+                if (child.permissions.canRead && child.permissions.canWrite) {
+                    let option: SelectOption = {
+                        text: child.component.name,
+                        value: child.component.id
+                    }
+                    processGroups.push(option);
+                }
+            });
+
+            this.controllerServiceActionProcessGroups = processGroups;
+            if (processGroups.length > 0) {
+                
this.moveControllerServiceForm.controls['processGroups'].setValue(processGroups[0].value);
+            }
+        });
+
+        // build the form
+        this.moveControllerServiceForm = this.formBuilder.group({
+            processGroups: new FormControl("Process Group", 
Validators.required)
+        });
+
+        this.store.dispatch(
+            setControllerService({
+                request: {
+                    controllerService: request.controllerService
+                }
+            })
+        );

Review Comment:
   I don't think this component needs to store the subject Controller Service 
in a store.



##########
nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.spec.ts:
##########
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MoveControllerService } from './move-controller-service.component';
+import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { provideMockStore } from '@ngrx/store/testing';
+import { initialState } from 
'../../../../state/contoller-service-state/controller-service-state.reducer';
+import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from 
'@angular/material/dialog';
+import { ComponentType, MoveControllerServiceDialogRequest } from 
'../../../../state/shared';
+
+describe('MoveControllerService', () => {

Review Comment:
   This test currently does not run.
   
   > Summary of all failing tests
   >  FAIL  
src/app/ui/common/controller-service/move-controller-service/move-controller-service.component.spec.ts
   >   ● MoveControllerService › should create



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to