diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java index 3e91e1d3ee..e185620a54 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/FlowAnalysisRulesEntity.java @@ -16,7 +16,12 @@ */ package org.apache.nifi.web.api.entity; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.apache.nifi.web.api.dto.util.TimeAdapter; + +import java.util.Date; import java.util.Set; /** @@ -25,6 +30,7 @@ import java.util.Set; @XmlRootElement(name = "flowAnalysisRulesEntity") public class FlowAnalysisRulesEntity extends Entity { + private Date currentTime; private Set flowAnalysisRules; /** @@ -38,4 +44,20 @@ public class FlowAnalysisRulesEntity extends Entity { this.flowAnalysisRules = flowAnalysisRules; } + /** + * @return current time on the server + */ + @XmlJavaTypeAdapter(TimeAdapter.class) + @Schema( + description = "The current time on the system.", + type = "string" + ) + public Date getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(Date currentTime) { + this.currentTime = currentTime; + } + } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java index 380f1c2895..3b46b6f968 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerResource.java @@ -978,6 +978,7 @@ public class ControllerResource extends ApplicationResource { // create the response entity final FlowAnalysisRulesEntity entity = new FlowAnalysisRulesEntity(); entity.setFlowAnalysisRules(flowAnalysisRules); + entity.setCurrentTime(new Date()); // generate the response return generateOkResponse(entity).build(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts index 16020b5edf..4baa347825 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts @@ -96,7 +96,6 @@ import { OkDialog } from '../../../../ui/common/ok-dialog/ok-dialog.component'; import { GroupComponents } from '../../ui/canvas/items/process-group/group-components/group-components.component'; import { EditProcessGroup } from '../../ui/canvas/items/process-group/edit-process-group/edit-process-group.component'; import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; -import * as ControllerServicesActions from '../controller-services/controller-services.actions'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; import { ControllerServiceService } from '../../service/controller-service.service'; import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; @@ -1057,17 +1056,6 @@ export class FlowEffects { .pipe( take(1), switchMap((createReponse) => { - // dispatch an inline create service success action so the new service is in the state - this.store.dispatch( - ControllerServicesActions.inlineCreateControllerServiceSuccess( - { - response: { - controllerService: createReponse - } - } - ) - ); - // fetch an updated property descriptor return this.flowService .getPropertyDescriptor(processorId, descriptor.name, false) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts index 85a5965a87..441a5bd06c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts @@ -67,7 +67,22 @@ const routes: Routes = [ } ] }, - { path: 'flow-analysis-rules', component: FlowAnalysisRules }, + { + path: 'flow-analysis-rules', + component: FlowAnalysisRules, + children: [ + { + path: ':id', + component: FlowAnalysisRules, + children: [ + { + path: 'edit', + component: FlowAnalysisRules + } + ] + } + ] + }, { path: 'registry-clients', component: RegistryClients, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts index ac7e9702c9..421a5b433c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts @@ -33,6 +33,7 @@ import { ReportingTasksModule } from '../ui/reporting-tasks/reporting-tasks.modu import { MatTabsModule } from '@angular/material/tabs'; import { ReportingTasksEffects } from '../state/reporting-tasks/reporting-tasks.effects'; import { RegistryClientsEffects } from '../state/registry-clients/registry-clients.effects'; +import { FlowAnalysisRulesEffects } from '../state/flow-analysis-rules/flow-analysis-rules.effects'; @NgModule({ declarations: [Settings], @@ -51,6 +52,7 @@ import { RegistryClientsEffects } from '../state/registry-clients/registry-clien GeneralEffects, ManagementControllerServicesEffects, ReportingTasksEffects, + FlowAnalysisRulesEffects, RegistryClientsEffects ), MatTabsModule diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts new file mode 100644 index 0000000000..b6f0a9ca14 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts @@ -0,0 +1,99 @@ +/* + * 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 { Observable } from 'rxjs'; +import { HttpClient } from '@angular/common/http'; +import { Client } from '../../../service/client.service'; +import { NiFiCommon } from '../../../service/nifi-common.service'; +import { + ConfigureFlowAnalysisRuleRequest, + CreateFlowAnalysisRuleRequest, + DeleteFlowAnalysisRuleRequest, + EnableFlowAnalysisRuleRequest, + FlowAnalysisRuleEntity +} from '../state/flow-analysis-rules'; + +@Injectable({ providedIn: 'root' }) +export class FlowAnalysisRuleService { + private static readonly API: string = '../nifi-api'; + + /** + * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling + * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. + * + * https://stackoverflow.com/a/59586462 + * + * @param url + * @private + */ + private stripProtocol(url: string): string { + return this.nifiCommon.substringAfterFirst(url, ':'); + } + + constructor( + private httpClient: HttpClient, + private client: Client, + private nifiCommon: NiFiCommon + ) {} + + getFlowAnalysisRule(): Observable { + return this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`); + } + + createFlowAnalysisRule(createFlowAnalysisRule: CreateFlowAnalysisRuleRequest): Observable { + return this.httpClient.post(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`, { + revision: createFlowAnalysisRule.revision, + component: { + bundle: createFlowAnalysisRule.flowAnalysisRuleBundle, + type: createFlowAnalysisRule.flowAnalysisRuleType + } + }); + } + + deleteFlowAnalysisRule(deleteFlowAnalysisRule: DeleteFlowAnalysisRuleRequest): Observable { + const entity: FlowAnalysisRuleEntity = deleteFlowAnalysisRule.flowAnalysisRule; + const revision: any = this.client.getRevision(entity); + return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + } + + getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable { + const params: any = { + propertyName, + sensitive + }; + return this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules/${id}/descriptors`, { + params + }); + } + + updateFlowAnalysisRule(configureFlowAnalysisRule: ConfigureFlowAnalysisRuleRequest): Observable { + return this.httpClient.put( + this.stripProtocol(configureFlowAnalysisRule.uri), + configureFlowAnalysisRule.payload + ); + } + + setEnable(flowAnalysisRule: EnableFlowAnalysisRuleRequest, enabled: boolean): Observable { + const entity: FlowAnalysisRuleEntity = flowAnalysisRule.flowAnalysisRule; + return this.httpClient.put(`${this.stripProtocol(entity.uri)}/run-status`, { + revision: this.client.getRevision(entity), + state: enabled ? 'ENABLED' : 'DISABLED', + uiOnly: true + }); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts new file mode 100644 index 0000000000..d783be1d47 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.actions.ts @@ -0,0 +1,123 @@ +/* + * 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 { createAction, props } from '@ngrx/store'; +import { + ConfigureFlowAnalysisRuleRequest, + ConfigureFlowAnalysisRuleSuccess, + CreateFlowAnalysisRuleRequest, + CreateFlowAnalysisRuleSuccess, + DeleteFlowAnalysisRuleRequest, + DeleteFlowAnalysisRuleSuccess, + EditFlowAnalysisRuleDialogRequest, + LoadFlowAnalysisRulesResponse, + SelectFlowAnalysisRuleRequest, + DisableFlowAnalysisRuleRequest, + EnableFlowAnalysisRuleRequest, + EnableFlowAnalysisRuleSuccess, + DisableFlowAnalysisRuleSuccess +} from './index'; + +export const resetFlowAnalysisRulesState = createAction('[Flow Analysis Rules] Reset Flow Analysis Rules State'); + +export const loadFlowAnalysisRules = createAction('[Flow Analysis Rules] Load Flow Analysis Rules'); + +export const loadFlowAnalysisRulesSuccess = createAction( + '[Flow Analysis Rules] Load Flow Analysis Rules Success', + props<{ response: LoadFlowAnalysisRulesResponse }>() +); + +export const openConfigureFlowAnalysisRuleDialog = createAction( + '[Flow Analysis Rules] Open Flow Analysis Rule Dialog', + props<{ request: EditFlowAnalysisRuleDialogRequest }>() +); + +export const configureFlowAnalysisRule = createAction( + '[Flow Analysis Rules] Configure Flow Analysis Rule', + props<{ request: ConfigureFlowAnalysisRuleRequest }>() +); + +export const configureFlowAnalysisRuleSuccess = createAction( + '[Flow Analysis Rules] Configure Flow Analysis Rule Success', + props<{ response: ConfigureFlowAnalysisRuleSuccess }>() +); + +export const enableFlowAnalysisRule = createAction( + '[Enable Flow Analysis Rule] Submit Enable Request', + props<{ + request: EnableFlowAnalysisRuleRequest; + }>() +); + +export const enableFlowAnalysisRuleSuccess = createAction( + '[Flow Analysis Rules] Enable Flow Analysis Rule Success', + props<{ response: EnableFlowAnalysisRuleSuccess }>() +); + +export const disableFlowAnalysisRule = createAction( + '[Enable Flow Analysis Rule] Submit Disable Request', + props<{ + request: DisableFlowAnalysisRuleRequest; + }>() +); + +export const disableFlowAnalysisRuleSuccess = createAction( + '[Flow Analysis Rules] Disable Flow Analysis Rule Success', + props<{ response: DisableFlowAnalysisRuleSuccess }>() +); + +export const flowAnalysisRuleApiError = createAction( + '[Flow Analysis Rules] Load Flow Analysis Rules Error', + props<{ error: string }>() +); + +export const openNewFlowAnalysisRuleDialog = createAction('[Flow Analysis Rules] Open New Flow Analysis Rule Dialog'); + +export const createFlowAnalysisRule = createAction( + '[Flow Analysis Rules] Create Flow Analysis Rule', + props<{ request: CreateFlowAnalysisRuleRequest }>() +); + +export const createFlowAnalysisRuleSuccess = createAction( + '[Flow Analysis Rules] Create Flow Analysis Rule Success', + props<{ response: CreateFlowAnalysisRuleSuccess }>() +); + +export const navigateToEditFlowAnalysisRule = createAction( + '[Flow Analysis Rules] Navigate To Edit Flow Analysis Rule', + props<{ id: string }>() +); + +export const promptFlowAnalysisRuleDeletion = createAction( + '[Flow Analysis Rules] Prompt Flow Analysis Rule Deletion', + props<{ request: DeleteFlowAnalysisRuleRequest }>() +); + +export const deleteFlowAnalysisRule = createAction( + '[Flow Analysis Rules] Delete Flow Analysis Rule', + props<{ request: DeleteFlowAnalysisRuleRequest }>() +); + +export const deleteFlowAnalysisRuleSuccess = createAction( + '[Flow Analysis Rules] Delete Flow Analysis Rule Success', + props<{ response: DeleteFlowAnalysisRuleSuccess }>() +); + +export const selectFlowAnalysisRule = createAction( + '[Flow Analysis Rules] Select Flow Analysis Rule', + props<{ request: SelectFlowAnalysisRuleRequest }>() +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts new file mode 100644 index 0000000000..b643383421 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts @@ -0,0 +1,524 @@ +/* + * 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 { Actions, createEffect, ofType } from '@ngrx/effects'; +import * as FlowAnalysisRuleActions from './flow-analysis-rules.actions'; +import { catchError, from, map, NEVER, Observable, of, switchMap, take, takeUntil, tap, withLatestFrom } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { NiFiState } from '../../../../state'; +import { selectFlowAnalysisRuleTypes } from '../../../../state/extension-types/extension-types.selectors'; +import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; +import { FlowAnalysisRuleService } from '../../service/flow-analysis-rule.service'; +import { Client } from '../../../../service/client.service'; +import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; +import { CreateFlowAnalysisRule } from '../../ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component'; +import { Router } from '@angular/router'; +import { selectSaving } from '../management-controller-services/management-controller-services.selectors'; +import { + InlineServiceCreationRequest, + InlineServiceCreationResponse, + NewPropertyDialogRequest, + NewPropertyDialogResponse, + Property, + PropertyDescriptor, + UpdateControllerServiceRequest +} from '../../../../state/shared'; +import { EditFlowAnalysisRule } from '../../ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component'; +import { CreateFlowAnalysisRuleSuccess } from './index'; +import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; +import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; +import { ExtensionTypesService } from '../../../../service/extension-types.service'; + +@Injectable() +export class FlowAnalysisRulesEffects { + constructor( + private actions$: Actions, + private store: Store, + private client: Client, + private managementControllerServiceService: ManagementControllerServiceService, + private extensionTypesService: ExtensionTypesService, + private flowAnalysisRuleService: FlowAnalysisRuleService, + private dialog: MatDialog, + private router: Router + ) {} + + loadFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.loadFlowAnalysisRules), + switchMap(() => + from(this.flowAnalysisRuleService.getFlowAnalysisRule()).pipe( + map((response) => + FlowAnalysisRuleActions.loadFlowAnalysisRulesSuccess({ + response: { + flowAnalysisRules: response.flowAnalysisRules, + loadedTimestamp: response.currentTime + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + openNewFlowAnalysisRuleDialog$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.openNewFlowAnalysisRuleDialog), + withLatestFrom(this.store.select(selectFlowAnalysisRuleTypes)), + tap(([action, flowAnalysisRuleTypes]) => { + this.dialog.open(CreateFlowAnalysisRule, { + data: { + flowAnalysisRuleTypes + }, + panelClass: 'medium-dialog' + }); + }) + ), + { dispatch: false } + ); + + createFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.createFlowAnalysisRule), + map((action) => action.request), + switchMap((request) => + from(this.flowAnalysisRuleService.createFlowAnalysisRule(request)).pipe( + map((response) => + FlowAnalysisRuleActions.createFlowAnalysisRuleSuccess({ + response: { + flowAnalysisRule: response + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + createFlowAnalysisRuleSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.createFlowAnalysisRuleSuccess), + map((action) => action.response), + tap(() => { + this.dialog.closeAll(); + }), + switchMap((response: CreateFlowAnalysisRuleSuccess) => + of( + FlowAnalysisRuleActions.selectFlowAnalysisRule({ + request: { + id: response.flowAnalysisRule.id + } + }) + ) + ) + ) + ); + + promptFlowAnalysisRuleDeletion$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.promptFlowAnalysisRuleDeletion), + map((action) => action.request), + tap((request) => { + const dialogReference = this.dialog.open(YesNoDialog, { + data: { + title: 'Delete Flow Analysis Rule', + message: `Delete reporting task ${request.flowAnalysisRule.component.name}?` + }, + panelClass: 'small-dialog' + }); + + dialogReference.componentInstance.yes.pipe(take(1)).subscribe(() => { + this.store.dispatch( + FlowAnalysisRuleActions.deleteFlowAnalysisRule({ + request + }) + ); + }); + }) + ), + { dispatch: false } + ); + + deleteFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.deleteFlowAnalysisRule), + map((action) => action.request), + switchMap((request) => + from(this.flowAnalysisRuleService.deleteFlowAnalysisRule(request)).pipe( + map((response) => + FlowAnalysisRuleActions.deleteFlowAnalysisRuleSuccess({ + response: { + flowAnalysisRule: response + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + navigateToEditFlowAnalysisRule$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.navigateToEditFlowAnalysisRule), + map((action) => action.id), + tap((id) => { + this.router.navigate(['/settings', 'flow-analysis-rules', id, 'edit']); + }) + ), + { dispatch: false } + ); + + openConfigureFlowAnalysisRuleDialog$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.openConfigureFlowAnalysisRuleDialog), + map((action) => action.request), + tap((request) => { + const ruleId: string = request.id; + + const editDialogReference = this.dialog.open(EditFlowAnalysisRule, { + data: { + flowAnalysisRule: request.flowAnalysisRule + }, + id: ruleId, + panelClass: 'large-dialog' + }); + + editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); + + editDialogReference.componentInstance.createNewProperty = ( + existingProperties: string[], + allowsSensitive: boolean + ): Observable => { + const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; + const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { + data: dialogRequest, + panelClass: 'small-dialog' + }); + + return newPropertyDialogReference.componentInstance.newProperty.pipe( + take(1), + switchMap((dialogResponse: NewPropertyDialogResponse) => { + return this.flowAnalysisRuleService + .getPropertyDescriptor(request.id, dialogResponse.name, dialogResponse.sensitive) + .pipe( + take(1), + map((response) => { + newPropertyDialogReference.close(); + + return { + property: dialogResponse.name, + value: null, + descriptor: response.propertyDescriptor + }; + }) + ); + }) + ); + }; + + const goTo = (commands: string[], destination: string): void => { + if (editDialogReference.componentInstance.editFlowAnalysisRuleForm.dirty) { + const saveChangesDialogReference = this.dialog.open(YesNoDialog, { + data: { + title: 'Flow Analysis Rule Configuration', + message: `Save changes before going to this ${destination}?` + }, + panelClass: 'small-dialog' + }); + + saveChangesDialogReference.componentInstance.yes.pipe(take(1)).subscribe(() => { + editDialogReference.componentInstance.submitForm(commands); + }); + + saveChangesDialogReference.componentInstance.no.pipe(take(1)).subscribe(() => { + editDialogReference.close('ROUTED'); + this.router.navigate(commands); + }); + } else { + editDialogReference.close('ROUTED'); + this.router.navigate(commands); + } + }; + + editDialogReference.componentInstance.goToService = (serviceId: string) => { + const commands: string[] = ['/settings', 'management-controller-services', serviceId]; + goTo(commands, 'Controller Service'); + }; + + editDialogReference.componentInstance.createNewService = ( + request: InlineServiceCreationRequest + ): Observable => { + const descriptor: PropertyDescriptor = request.descriptor; + + // fetch all services that implement the requested service api + return this.extensionTypesService + .getImplementingControllerServiceTypes( + // @ts-ignore + descriptor.identifiesControllerService, + descriptor.identifiesControllerServiceBundle + ) + .pipe( + take(1), + switchMap((implementingTypesResponse) => { + // show the create controller service dialog with the types that implemented the interface + const createServiceDialogReference = this.dialog.open(CreateControllerService, { + data: { + controllerServiceTypes: implementingTypesResponse.controllerServiceTypes + }, + panelClass: 'medium-dialog' + }); + + return createServiceDialogReference.componentInstance.createControllerService.pipe( + take(1), + switchMap((controllerServiceType) => { + // typically this sequence would be implemented with ngrx actions, however we are + // currently in an edit session and we need to return both the value (new service id) + // and updated property descriptor so the table renders correctly + return this.managementControllerServiceService + .createControllerService({ + revision: { + clientId: this.client.getClientId(), + version: 0 + }, + controllerServiceType: controllerServiceType.type, + controllerServiceBundle: controllerServiceType.bundle + }) + .pipe( + take(1), + switchMap((createResponse) => { + // fetch an updated property descriptor + return this.flowAnalysisRuleService + .getPropertyDescriptor(ruleId, descriptor.name, false) + .pipe( + take(1), + map((descriptorResponse) => { + createServiceDialogReference.close(); + + return { + value: createResponse.id, + descriptor: + descriptorResponse.propertyDescriptor + }; + }) + ); + }), + catchError((error) => { + // TODO - show error + return NEVER; + }) + ); + }) + ); + }) + ); + }; + + editDialogReference.componentInstance.editFlowAnalysisRule + .pipe(takeUntil(editDialogReference.afterClosed())) + .subscribe((updateControllerServiceRequest: UpdateControllerServiceRequest) => { + this.store.dispatch( + FlowAnalysisRuleActions.configureFlowAnalysisRule({ + request: { + id: request.flowAnalysisRule.id, + uri: request.flowAnalysisRule.uri, + payload: updateControllerServiceRequest.payload, + postUpdateNavigation: updateControllerServiceRequest.postUpdateNavigation + } + }) + ); + }); + + editDialogReference.afterClosed().subscribe((response) => { + if (response != 'ROUTED') { + this.store.dispatch( + FlowAnalysisRuleActions.selectFlowAnalysisRule({ + request: { + id: ruleId + } + }) + ); + } + }); + }) + ), + { dispatch: false } + ); + + configureFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.configureFlowAnalysisRule), + map((action) => action.request), + switchMap((request) => + from(this.flowAnalysisRuleService.updateFlowAnalysisRule(request)).pipe( + map((response) => + FlowAnalysisRuleActions.configureFlowAnalysisRuleSuccess({ + response: { + id: request.id, + flowAnalysisRule: response, + postUpdateNavigation: request.postUpdateNavigation + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + configureFlowAnalysisRuleSuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.configureFlowAnalysisRuleSuccess), + map((action) => action.response), + tap((response) => { + if (response.postUpdateNavigation) { + this.router.navigate(response.postUpdateNavigation); + this.dialog.getDialogById(response.id)?.close('ROUTED'); + } else { + this.dialog.closeAll(); + } + }) + ), + { dispatch: false } + ); + + selectFlowAnalysisRule$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.selectFlowAnalysisRule), + map((action) => action.request), + tap((request) => { + this.router.navigate(['/settings', 'flow-analysis-rules', request.id]); + }) + ), + { dispatch: false } + ); + + enableFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.enableFlowAnalysisRule), + map((action) => action.request), + switchMap((request) => + from(this.flowAnalysisRuleService.setEnable(request, true)).pipe( + map((response) => + FlowAnalysisRuleActions.enableFlowAnalysisRuleSuccess({ + response: { + id: request.id, + flowAnalysisRule: response, + postUpdateNavigation: response.postUpdateNavigation + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + enableFlowAnalysisRuleSuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.enableFlowAnalysisRuleSuccess), + map((action) => action.response), + tap((response) => { + if (response.postUpdateNavigation) { + this.router.navigate(response.postUpdateNavigation); + } + }) + ), + { dispatch: false } + ); + + disableFlowAnalysisRule$ = createEffect(() => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.disableFlowAnalysisRule), + map((action) => action.request), + switchMap((request) => + from(this.flowAnalysisRuleService.setEnable(request, false)).pipe( + map((response) => + FlowAnalysisRuleActions.disableFlowAnalysisRuleSuccess({ + response: { + id: request.id, + flowAnalysisRule: response, + postUpdateNavigation: response.postUpdateNavigation + } + }) + ), + catchError((error) => + of( + FlowAnalysisRuleActions.flowAnalysisRuleApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + disableFlowAnalysisRuleSuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(FlowAnalysisRuleActions.disableFlowAnalysisRuleSuccess), + map((action) => action.response), + tap((response) => { + if (response.postUpdateNavigation) { + this.router.navigate(response.postUpdateNavigation); + } + }) + ), + { dispatch: false } + ); +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts new file mode 100644 index 0000000000..e7202b2408 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.reducer.ts @@ -0,0 +1,123 @@ +/* + * 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 { createReducer, on } from '@ngrx/store'; +import { FlowAnalysisRulesState } from './index'; +import { + configureFlowAnalysisRule, + configureFlowAnalysisRuleSuccess, + createFlowAnalysisRule, + createFlowAnalysisRuleSuccess, + deleteFlowAnalysisRule, + deleteFlowAnalysisRuleSuccess, + loadFlowAnalysisRules, + loadFlowAnalysisRulesSuccess, + flowAnalysisRuleApiError, + resetFlowAnalysisRulesState, + disableFlowAnalysisRule, + enableFlowAnalysisRule, + enableFlowAnalysisRuleSuccess, + disableFlowAnalysisRuleSuccess +} from './flow-analysis-rules.actions'; +import { produce } from 'immer'; + +export const initialState: FlowAnalysisRulesState = { + flowAnalysisRules: [], + saving: false, + loadedTimestamp: '', + error: null, + status: 'pending' +}; + +export const flowAnalysisRulesReducer = createReducer( + initialState, + on(resetFlowAnalysisRulesState, (state) => ({ + ...initialState + })), + on(loadFlowAnalysisRules, (state) => ({ + ...state, + status: 'loading' as const + })), + on(loadFlowAnalysisRulesSuccess, (state, { response }) => ({ + ...state, + flowAnalysisRules: response.flowAnalysisRules, + loadedTimestamp: response.loadedTimestamp, + error: null, + status: 'success' as const + })), + on(flowAnalysisRuleApiError, (state, { error }) => ({ + ...state, + saving: false, + error, + status: 'error' as const + })), + on(enableFlowAnalysisRuleSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const componentIndex: number = draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id); + if (componentIndex > -1) { + draftState.flowAnalysisRules[componentIndex] = response.flowAnalysisRule; + } + draftState.saving = false; + }); + }), + on(disableFlowAnalysisRuleSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const componentIndex: number = draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id); + if (componentIndex > -1) { + draftState.flowAnalysisRules[componentIndex] = response.flowAnalysisRule; + } + draftState.saving = false; + }); + }), + on(configureFlowAnalysisRuleSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const componentIndex: number = draftState.flowAnalysisRules.findIndex((f: any) => response.id === f.id); + if (componentIndex > -1) { + draftState.flowAnalysisRules[componentIndex] = response.flowAnalysisRule; + } + draftState.saving = false; + }); + }), + on( + createFlowAnalysisRule, + deleteFlowAnalysisRule, + configureFlowAnalysisRule, + enableFlowAnalysisRule, + disableFlowAnalysisRule, + (state, { request }) => ({ + ...state, + saving: true + }) + ), + on(createFlowAnalysisRuleSuccess, (state, { response }) => { + return produce(state, (draftState) => { + draftState.flowAnalysisRules.push(response.flowAnalysisRule); + draftState.saving = false; + }); + }), + on(deleteFlowAnalysisRuleSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const componentIndex: number = draftState.flowAnalysisRules.findIndex( + (f: any) => response.flowAnalysisRule.id === f.id + ); + if (componentIndex > -1) { + draftState.flowAnalysisRules.splice(componentIndex, 1); + } + draftState.saving = false; + }); + }) +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts new file mode 100644 index 0000000000..b69f9f2ae2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.selectors.ts @@ -0,0 +1,54 @@ +/* + * 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 { createSelector } from '@ngrx/store'; +import { selectSettingsState, SettingsState } from '../index'; +import { FlowAnalysisRuleEntity, flowAnalysisRulesFeatureKey, FlowAnalysisRulesState } from './index'; +import { selectCurrentRoute } from '../../../../state/router/router.selectors'; + +export const selectFlowAnalysisRulesState = createSelector( + selectSettingsState, + (state: SettingsState) => state[flowAnalysisRulesFeatureKey] +); + +export const selectSaving = createSelector( + selectFlowAnalysisRulesState, + (state: FlowAnalysisRulesState) => state.saving +); + +export const selectFlowAnalysisRuleIdFromRoute = createSelector(selectCurrentRoute, (route) => { + if (route) { + // always select the rule from the route + return route.params.id; + } + return null; +}); + +export const selectSingleEditedFlowAnalysisRule = createSelector(selectCurrentRoute, (route) => { + if (route?.routeConfig?.path == 'edit') { + return route.params.id; + } + return null; +}); + +export const selectFlowAnalysisRules = createSelector( + selectFlowAnalysisRulesState, + (state: FlowAnalysisRulesState) => state.flowAnalysisRules +); + +export const selectRule = (id: string) => + createSelector(selectFlowAnalysisRules, (tasks: FlowAnalysisRuleEntity[]) => tasks.find((task) => id == task.id)); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts new file mode 100644 index 0000000000..043230f9cf --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/index.ts @@ -0,0 +1,122 @@ +/* + * 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 { BulletinEntity, Bundle, DocumentedType, Permissions, Revision } from '../../../../state/shared'; + +export const flowAnalysisRulesFeatureKey = 'flowAnalysisRules'; + +export interface CreateFlowAnalysisRuleDialogRequest { + flowAnalysisRuleTypes: DocumentedType[]; +} + +export interface LoadFlowAnalysisRulesResponse { + flowAnalysisRules: FlowAnalysisRuleEntity[]; + loadedTimestamp: string; +} + +export interface CreateFlowAnalysisRuleRequest { + flowAnalysisRuleType: string; + flowAnalysisRuleBundle: Bundle; + revision: Revision; +} + +export interface CreateFlowAnalysisRuleSuccess { + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface ConfigureFlowAnalysisRuleRequest { + id: string; + uri: string; + payload: any; + postUpdateNavigation?: string[]; +} + +export interface ConfigureFlowAnalysisRuleSuccess { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; + postUpdateNavigation?: string[]; +} + +export interface UpdateFlowAnalysisRuleRequest { + payload: any; + postUpdateNavigation?: string[]; +} + +export interface EnableFlowAnalysisRuleSuccess { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; + postUpdateNavigation?: string[]; +} + +export interface DisableFlowAnalysisRuleSuccess { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; + postUpdateNavigation?: string[]; +} + +export interface EnableFlowAnalysisRuleRequest { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface DisableFlowAnalysisRuleRequest { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface ConfigureFlowAnalysisRuleRequest { + id: string; + uri: string; + payload: any; + postUpdateNavigation?: string[]; +} + +export interface EditFlowAnalysisRuleDialogRequest { + id: string; + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface DeleteFlowAnalysisRuleRequest { + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface DeleteFlowAnalysisRuleSuccess { + flowAnalysisRule: FlowAnalysisRuleEntity; +} + +export interface SelectFlowAnalysisRuleRequest { + id: string; +} + +export interface FlowAnalysisRuleEntity { + permissions: Permissions; + operatePermissions?: Permissions; + revision: Revision; + bulletins: BulletinEntity[]; + id: string; + uri: string; + status: any; + component: any; +} + +export interface FlowAnalysisRulesState { + flowAnalysisRules: FlowAnalysisRuleEntity[]; + saving: boolean; + loadedTimestamp: string; + error: string | null; + status: 'pending' | 'loading' | 'error' | 'success'; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts index 09a7a40156..1182b01024 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts @@ -27,6 +27,8 @@ import { reportingTasksFeatureKey, ReportingTasksState } from './reporting-tasks import { reportingTasksReducer } from './reporting-tasks/reporting-tasks.reducer'; import { registryClientsFeatureKey, RegistryClientsState } from './registry-clients'; import { registryClientsReducer } from './registry-clients/registry-clients.reducer'; +import { flowAnalysisRulesFeatureKey, FlowAnalysisRulesState } from './flow-analysis-rules'; +import { flowAnalysisRulesReducer } from './flow-analysis-rules/flow-analysis-rules.reducer'; export const settingsFeatureKey = 'settings'; @@ -34,6 +36,7 @@ export interface SettingsState { [generalFeatureKey]: GeneralState; [managementControllerServicesFeatureKey]: ManagementControllerServicesState; [reportingTasksFeatureKey]: ReportingTasksState; + [flowAnalysisRulesFeatureKey]: FlowAnalysisRulesState; [registryClientsFeatureKey]: RegistryClientsState; } @@ -42,6 +45,7 @@ export function reducers(state: SettingsState | undefined, action: Action) { [generalFeatureKey]: generalReducer, [managementControllerServicesFeatureKey]: managementControllerServicesReducer, [reportingTasksFeatureKey]: reportingTasksReducer, + [flowAnalysisRulesFeatureKey]: flowAnalysisRulesReducer, [registryClientsFeatureKey]: registryClientsReducer })(state, action); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts index bd3022812e..f097a08dc8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/index.ts @@ -58,6 +58,11 @@ export interface ConfigureReportingTaskRequest { postUpdateNavigation?: string[]; } +export interface UpdateReportingTaskRequest { + payload: any; + postUpdateNavigation?: string[]; +} + export interface EditReportingTaskDialogRequest { id: string; reportingTask: ReportingTaskEntity; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts index c08215a8ad..12ba1183d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts @@ -43,7 +43,6 @@ import { CreateReportingTaskSuccess } from './index'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; -import * as ManagementControllerServicesActions from '../management-controller-services/management-controller-services.actions'; import { Client } from '../../../../service/client.service'; @Injectable() @@ -261,7 +260,7 @@ export class ReportingTasksEffects { if (editDialogReference.componentInstance.editReportingTaskForm.dirty) { const saveChangesDialogReference = this.dialog.open(YesNoDialog, { data: { - title: 'Controller Service Configuration', + title: 'Reporting Task Configuration', message: `Save changes before going to this ${destination}?` }, panelClass: 'small-dialog' @@ -327,17 +326,6 @@ export class ReportingTasksEffects { .pipe( take(1), switchMap((createResponse) => { - // dispatch an inline create service success action so the new service is in the state - this.store.dispatch( - ManagementControllerServicesActions.inlineCreateControllerServiceSuccess( - { - response: { - controllerService: createResponse - } - } - ) - ); - // fetch an updated property descriptor return this.reportingTaskService .getPropertyDescriptor(taskId, descriptor.name, false) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html new file mode 100644 index 0000000000..ac46c8b944 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.html @@ -0,0 +1,22 @@ + + + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss new file mode 100644 index 0000000000..2944f98194 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.scss @@ -0,0 +1,16 @@ +/* + * 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. + */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.ts new file mode 100644 index 0000000000..35b4ae82a0 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.spec.ts @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateFlowAnalysisRule } from './create-flow-analysis-rule.component'; +import { CreateFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { provideMockStore } from '@ngrx/store/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { initialState } from '../../../state/flow-analysis-rules/flow-analysis-rules.reducer'; + +describe('CreateFlowAnalysisRule', () => { + let component: CreateFlowAnalysisRule; + let fixture: ComponentFixture; + + const data: CreateFlowAnalysisRuleDialogRequest = { + flowAnalysisRuleTypes: [ + { + type: 'org.apache.nifi.flowanalysis.rules.DisallowComponentType', + bundle: { + group: 'org.apache.nifi', + artifact: 'nifi-standard-nar', + version: '2.0.0-SNAPSHOT' + }, + description: + 'Produces rule violations for each component (i.e. processors or controller services) of a given type.', + restricted: false, + tags: ['component', 'controller service', 'type', 'processor'] + } + ] + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CreateFlowAnalysisRule, BrowserAnimationsModule], + providers: [{ provide: MAT_DIALOG_DATA, useValue: data }, provideMockStore({ initialState })] + }); + fixture = TestBed.createComponent(CreateFlowAnalysisRule); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts new file mode 100644 index 0000000000..6c8c5f83bc --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component.ts @@ -0,0 +1,62 @@ +/* + * 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 } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { Store } from '@ngrx/store'; +import { ExtensionCreation } from '../../../../../ui/common/extension-creation/extension-creation.component'; +import { CreateFlowAnalysisRuleDialogRequest, FlowAnalysisRulesState } from '../../../state/flow-analysis-rules'; +import { createFlowAnalysisRule } from '../../../state/flow-analysis-rules/flow-analysis-rules.actions'; +import { Client } from '../../../../../service/client.service'; +import { DocumentedType } from '../../../../../state/shared'; +import { selectSaving } from '../../../state/flow-analysis-rules/flow-analysis-rules.selectors'; +import { AsyncPipe } from '@angular/common'; + +@Component({ + selector: 'create-flow-analysis-rule', + standalone: true, + imports: [ExtensionCreation, AsyncPipe], + templateUrl: './create-flow-analysis-rule.component.html', + styleUrls: ['./create-flow-analysis-rule.component.scss'] +}) +export class CreateFlowAnalysisRule { + flowAnalysisRules: DocumentedType[]; + saving$ = this.store.select(selectSaving); + + constructor( + @Inject(MAT_DIALOG_DATA) private dialogRequest: CreateFlowAnalysisRuleDialogRequest, + private store: Store, + private client: Client + ) { + this.flowAnalysisRules = dialogRequest.flowAnalysisRuleTypes; + } + + createFlowAnalysisRule(flowAnalysisRuleType: DocumentedType): void { + this.store.dispatch( + createFlowAnalysisRule({ + request: { + revision: { + clientId: this.client.getClientId(), + version: 0 + }, + flowAnalysisRuleType: flowAnalysisRuleType.type, + flowAnalysisRuleBundle: flowAnalysisRuleType.bundle + } + }) + ); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.html new file mode 100644 index 0000000000..ef6270d2aa --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.html @@ -0,0 +1,93 @@ + + +

Edit Flow Analysis Rule

+
+ + + +
+
+ + Name + + +
+
Id
+
{{ request.flowAnalysisRule.id }}
+
+
+
Type
+
{{ formatType(request.flowAnalysisRule) }}
+
+
+
Bundle
+
{{ formatBundle(request.flowAnalysisRule) }}
+
+
+
+
+ + Enforcement Policy + + + {{ option.text }} + + + +
+
+
+
+ +
+ + +
+
+ +
+ + Comments + + +
+
+
+
+ + + + +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss new file mode 100644 index 0000000000..5f0d7da2a1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.scss @@ -0,0 +1,43 @@ +/* + * 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. + */ + +@use '@angular/material' as mat; + +.flow-analysis-rule-edit-form { + @include mat.button-density(-1); + + .mdc-dialog__content { + padding: 0 16px; + font-size: 14px; + + .tab-content { + height: 475px; + overflow-y: auto; + } + } + + .mat-mdc-form-field { + width: 100%; + } + + .supports-flow-analysis-rules { + ul { + list-style: disc outside; + margin-left: 1em; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts new file mode 100644 index 0000000000..40c8b8222d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.spec.ts @@ -0,0 +1,105 @@ +/* + * 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 { EditFlowAnalysisRule } from './edit-flow-analysis-rule.component'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { EditFlowAnalysisRuleDialogRequest } from '../../../state/flow-analysis-rules'; + +describe('EditFlowAnalysisRule', () => { + let component: EditFlowAnalysisRule; + let fixture: ComponentFixture; + + const data: EditFlowAnalysisRuleDialogRequest = { + id: 'd5142be7-018c-1000-7105-2b1163fe0355', + flowAnalysisRule: { + revision: { + clientId: '2be7f8d0-fad2-4909-918f-b9a4ef1675b2', + version: 3 + }, + id: 'f08ddf27-018c-1000-4970-2fa78a6ee3ed', + uri: 'https://localhost:8443/nifi-api/controller/flow-analysis-rules/f08ddf27-018c-1000-4970-2fa78a6ee3ed', + permissions: { + canRead: true, + canWrite: true + }, + bulletins: [], + component: { + id: 'f08ddf27-018c-1000-4970-2fa78a6ee3ed', + name: 'DisallowComponentType', + type: 'org.apache.nifi.flowanalysis.rules.DisallowComponentType', + bundle: { + group: 'org.apache.nifi', + artifact: 'nifi-standard-nar', + version: '2.0.0-SNAPSHOT' + }, + state: 'DISABLED', + comments: 'dfghsdgh', + persistsState: false, + restricted: false, + deprecated: false, + multipleVersionsAvailable: false, + supportsSensitiveDynamicProperties: false, + enforcementPolicy: 'ENFORCE', + properties: { + 'component-type': null + }, + descriptors: { + 'component-type': { + name: 'component-type', + displayName: 'Component Type', + description: + "Components of the given type will produce a rule violation (i.e. they shouldn't exist). Either the simple or the fully qualified name of the type should be provided.", + required: true, + sensitive: false, + dynamic: false, + supportsEl: false, + expressionLanguageScope: 'Not Supported', + dependencies: [] + } + }, + validationErrors: ["'Component Type' is invalid because Component Type is required"], + validationStatus: 'INVALID', + extensionMissing: false + }, + operatePermissions: { + canRead: true, + canWrite: true + }, + status: { + runStatus: 'DISABLED', + validationStatus: 'INVALID' + } + } + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [EditFlowAnalysisRule, BrowserAnimationsModule], + providers: [{ provide: MAT_DIALOG_DATA, useValue: data }] + }); + fixture = TestBed.createComponent(EditFlowAnalysisRule); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts new file mode 100644 index 0000000000..d55f09fa05 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component.ts @@ -0,0 +1,161 @@ +/* + * 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, EventEmitter, Inject, Input, Output, signal } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { AsyncPipe, NgForOf, NgIf } from '@angular/common'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatOptionModule } from '@angular/material/core'; +import { MatSelectModule } from '@angular/material/select'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Observable } from 'rxjs'; +import { Client } from '../../../../../service/client.service'; +import { + InlineServiceCreationRequest, + InlineServiceCreationResponse, + Property, + SelectOption, + TextTipInput +} from '../../../../../state/shared'; +import { NiFiCommon } from '../../../../../service/nifi-common.service'; +import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component'; +import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive'; +import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive'; +import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component'; +import { + EditFlowAnalysisRuleDialogRequest, + FlowAnalysisRuleEntity, + UpdateFlowAnalysisRuleRequest +} from '../../../state/flow-analysis-rules'; +import { FlowAnalysisRuleTable } from '../flow-analysis-rule-table/flow-analysis-rule-table.component'; + +@Component({ + selector: 'edit-flow-analysis-rule', + standalone: true, + templateUrl: './edit-flow-analysis-rule.component.html', + imports: [ + ReactiveFormsModule, + MatDialogModule, + MatInputModule, + MatButtonModule, + NgIf, + MatTabsModule, + MatOptionModule, + MatSelectModule, + NgForOf, + PropertyTable, + AsyncPipe, + NifiSpinnerDirective, + MatTooltipModule, + NifiTooltipDirective, + FlowAnalysisRuleTable + ], + styleUrls: ['./edit-flow-analysis-rule.component.scss'] +}) +export class EditFlowAnalysisRule { + @Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable; + @Input() createNewService!: (request: InlineServiceCreationRequest) => Observable; + @Input() saving$!: Observable; + @Input() goToService!: (serviceId: string) => void; + @Output() editFlowAnalysisRule: EventEmitter = + new EventEmitter(); + + editFlowAnalysisRuleForm: FormGroup; + + strategies: SelectOption[] = [ + { + text: 'Enforce', + value: 'ENFORCE', + description: 'Treat violations of this rule as errors the correction of which is mandatory.' + } + ]; + + constructor( + @Inject(MAT_DIALOG_DATA) public request: EditFlowAnalysisRuleDialogRequest, + private formBuilder: FormBuilder, + private client: Client, + private nifiCommon: NiFiCommon + ) { + const serviceProperties: any = request.flowAnalysisRule.component.properties; + const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => { + const [property, value] = entry; + return { + property, + value, + descriptor: request.flowAnalysisRule.component.descriptors[property] + }; + }); + + // build the form + this.editFlowAnalysisRuleForm = this.formBuilder.group({ + name: new FormControl(request.flowAnalysisRule.component.name, Validators.required), + state: new FormControl(request.flowAnalysisRule.component.state === 'STOPPED', Validators.required), + enforcementPolicy: new FormControl('ENFORCE', Validators.required), + properties: new FormControl(properties), + comments: new FormControl(request.flowAnalysisRule.component.comments) + }); + } + + formatType(entity: FlowAnalysisRuleEntity): string { + return this.nifiCommon.formatType(entity.component); + } + + formatBundle(entity: FlowAnalysisRuleEntity): string { + return this.nifiCommon.formatBundle(entity.component.bundle); + } + + submitForm(postUpdateNavigation?: string[]) { + const payload: any = { + revision: this.client.getRevision(this.request.flowAnalysisRule), + component: { + id: this.request.flowAnalysisRule.id, + name: this.editFlowAnalysisRuleForm.get('name')?.value, + comments: this.editFlowAnalysisRuleForm.get('comments')?.value, + enforcementPolicy: this.editFlowAnalysisRuleForm.get('enforcementPolicy')?.value, + state: this.editFlowAnalysisRuleForm.get('state')?.value ? 'STOPPED' : 'DISABLED' + } + }; + + const propertyControl: AbstractControl | null = this.editFlowAnalysisRuleForm.get('properties'); + if (propertyControl && propertyControl.dirty) { + const properties: Property[] = propertyControl.value; + const values: { [key: string]: string | null } = {}; + properties.forEach((property) => (values[property.property] = property.value)); + payload.component.properties = values; + payload.component.sensitiveDynamicPropertyNames = properties + .filter((property) => property.descriptor.dynamic && property.descriptor.sensitive) + .map((property) => property.descriptor.name); + } + + this.editFlowAnalysisRule.next({ + payload, + postUpdateNavigation + }); + } + + getPropertyTipData(option: SelectOption): TextTipInput { + return { + // @ts-ignore + text: option.description + }; + } + + protected readonly TextTip = TextTip; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html new file mode 100644 index 0000000000..32802aa136 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html @@ -0,0 +1,151 @@ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
Name + + {{ item.component.name }} + + +
{{ item.id }}
+
+
Type + + {{ formatType(item) }} + + Bundle + + {{ formatBundle(item) }} + + State +
+
+
{{ formatState(item) }}
+
({{ item.status.activeThreadCount }})
+
+
+
+
+ +
+
+
+
+
+
+
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss new file mode 100644 index 0000000000..fad04378a6 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.scss @@ -0,0 +1,24 @@ +/* + * 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. + */ + +.flow-analysis-rule-table.listing-table { + table { + .mat-column-moreDetails { + min-width: 100px; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts new file mode 100644 index 0000000000..0d9de74b27 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.spec.ts @@ -0,0 +1,40 @@ +/* + * 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 { FlowAnalysisRuleTable } from './flow-analysis-rule-table.component'; +import { MatTableModule } from '@angular/material/table'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +describe('FlowAnalysisRuleTable', () => { + let component: FlowAnalysisRuleTable; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, MatTableModule, FlowAnalysisRuleTable] + }); + fixture = TestBed.createComponent(FlowAnalysisRuleTable); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts new file mode 100644 index 0000000000..5108c5d352 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts @@ -0,0 +1,268 @@ +/* + * 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, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { RouterLink } from '@angular/router'; +import { NgClass, NgIf } from '@angular/common'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { FlowAnalysisRuleEntity } from '../../../state/flow-analysis-rules'; +import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component'; +import { BulletinsTip } from '../../../../../ui/common/tooltips/bulletins-tip/bulletins-tip.component'; +import { ValidationErrorsTip } from '../../../../../ui/common/tooltips/validation-errors-tip/validation-errors-tip.component'; +import { NiFiCommon } from '../../../../../service/nifi-common.service'; +import { BulletinsTipInput, TextTipInput, ValidationErrorsTipInput } from '../../../../../state/shared'; +import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive'; +import { ReportingTaskEntity } from '../../../state/reporting-tasks'; +import { CurrentUser } from '../../../../../state/current-user'; + +@Component({ + selector: 'flow-analysis-rule-table', + standalone: true, + templateUrl: './flow-analysis-rule-table.component.html', + imports: [ + MatButtonModule, + MatDialogModule, + MatTableModule, + MatSortModule, + NgIf, + NgClass, + NifiTooltipDirective, + RouterLink + ], + styleUrls: ['./flow-analysis-rule-table.component.scss', '../../../../../../assets/styles/listing-table.scss'] +}) +export class FlowAnalysisRuleTable { + @Input() set flowAnalysisRules(flowAnalysisRuleEntities: FlowAnalysisRuleEntity[]) { + this.dataSource.data = this.sortFlowAnalysisRules(flowAnalysisRuleEntities, this.sort); + } + @Input() selectedFlowAnalysisRuleId!: string; + @Input() currentUser!: CurrentUser; + + @Output() selectFlowAnalysisRule: EventEmitter = new EventEmitter(); + @Output() deleteFlowAnalysisRule: EventEmitter = new EventEmitter(); + @Output() configureFlowAnalysisRule: EventEmitter = + new EventEmitter(); + @Output() enableFlowAnalysisRule: EventEmitter = new EventEmitter(); + @Output() disableFlowAnalysisRule: EventEmitter = + new EventEmitter(); + + sort: Sort = { + active: 'name', + direction: 'asc' + }; + + protected readonly TextTip = TextTip; + protected readonly BulletinsTip = BulletinsTip; + protected readonly ValidationErrorsTip = ValidationErrorsTip; + + displayedColumns: string[] = ['moreDetails', 'name', 'type', 'bundle', 'state', 'actions']; + dataSource: MatTableDataSource = new MatTableDataSource(); + + constructor(private nifiCommon: NiFiCommon) {} + + updateSort(sort: Sort): void { + this.sort = sort; + this.dataSource.data = this.sortFlowAnalysisRules(this.dataSource.data, sort); + } + + sortFlowAnalysisRules(items: FlowAnalysisRuleEntity[], sort: Sort): FlowAnalysisRuleEntity[] { + const data: FlowAnalysisRuleEntity[] = items.slice(); + return data.sort((a, b) => { + const isAsc = sort.direction === 'asc'; + + let retVal: number = 0; + switch (sort.active) { + case 'name': + retVal = this.nifiCommon.compareString(a.component.name, b.component.name); + break; + case 'type': + retVal = this.nifiCommon.compareString(this.formatType(a), this.formatType(b)); + break; + case 'bundle': + retVal = this.nifiCommon.compareString(this.formatBundle(a), this.formatBundle(b)); + break; + case 'state': + retVal = this.nifiCommon.compareString(this.formatState(a), this.formatState(b)); + break; + } + + return retVal * (isAsc ? 1 : -1); + }); + } + + canRead(entity: FlowAnalysisRuleEntity): boolean { + return entity.permissions.canRead; + } + + canWrite(entity: FlowAnalysisRuleEntity): boolean { + return entity.permissions.canWrite; + } + + canOperate(entity: FlowAnalysisRuleEntity): boolean { + if (this.canWrite(entity)) { + return true; + } + return !!entity.operatePermissions?.canWrite; + } + + hasComments(entity: FlowAnalysisRuleEntity): boolean { + return !this.nifiCommon.isBlank(entity.component.comments); + } + + getCommentsTipData(entity: FlowAnalysisRuleEntity): TextTipInput { + return { + text: entity.component.comments + }; + } + + hasErrors(entity: FlowAnalysisRuleEntity): boolean { + return !this.nifiCommon.isEmpty(entity.component.validationErrors); + } + + getValidationErrorsTipData(entity: FlowAnalysisRuleEntity): ValidationErrorsTipInput { + return { + isValidating: entity.status.validationStatus === 'VALIDATING', + validationErrors: entity.component.validationErrors + }; + } + + hasBulletins(entity: FlowAnalysisRuleEntity): boolean { + return !this.nifiCommon.isEmpty(entity.bulletins); + } + + getBulletinsTipData(entity: FlowAnalysisRuleEntity): BulletinsTipInput { + return { + bulletins: entity.bulletins + }; + } + + getStateIcon(entity: FlowAnalysisRuleEntity): string { + if (entity.status.validationStatus === 'VALIDATING') { + return 'validating fa fa-spin fa-circle-o-notch'; + } else if (entity.status.validationStatus === 'INVALID') { + return 'invalid fa fa-warning'; + } else { + if (entity.status.runStatus === 'DISABLED') { + return 'disabled icon icon-enable-false'; + } else if (entity.status.runStatus === 'ENABLED') { + return 'enabled fa fa-flash'; + } + } + return ''; + } + + formatState(entity: FlowAnalysisRuleEntity): string { + if (entity.status.validationStatus === 'VALIDATING') { + return 'Validating'; + } else if (entity.status.validationStatus === 'INVALID') { + return 'Invalid'; + } else { + if (entity.status.runStatus === 'DISABLED') { + return 'Disabled'; + } else if (entity.status.runStatus === 'ENABLED') { + return 'Enabled'; + } + } + return ''; + } + + formatType(entity: FlowAnalysisRuleEntity): string { + return this.nifiCommon.formatType(entity.component); + } + + formatBundle(entity: FlowAnalysisRuleEntity): string { + return this.nifiCommon.formatBundle(entity.component.bundle); + } + + isDisabled(entity: FlowAnalysisRuleEntity): boolean { + return entity.status.runStatus === 'DISABLED'; + } + + isEnabled(entity: FlowAnalysisRuleEntity): boolean { + return entity.status.runStatus === 'ENABLED'; + } + + hasActiveThreads(entity: ReportingTaskEntity): boolean { + return entity.status?.activeThreadCount > 0; + } + + canConfigure(entity: FlowAnalysisRuleEntity): boolean { + return this.canRead(entity) && this.canWrite(entity) && this.isDisabled(entity); + } + + configureClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void { + event.stopPropagation(); + this.configureFlowAnalysisRule.next(entity); + } + + canEnable(entity: FlowAnalysisRuleEntity): boolean { + const userAuthorized: boolean = this.canRead(entity) && this.canOperate(entity); + return userAuthorized && this.isDisabled(entity) && entity.status.validationStatus === 'VALID'; + } + + enabledClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void { + this.enableFlowAnalysisRule.next(entity); + } + + canDisable(entity: FlowAnalysisRuleEntity): boolean { + const userAuthorized: boolean = this.canRead(entity) && this.canOperate(entity); + return userAuthorized && this.isEnabled(entity); + } + + disableClicked(entity: FlowAnalysisRuleEntity, event: MouseEvent): void { + this.disableFlowAnalysisRule.next(entity); + } + + canChangeVersion(entity: FlowAnalysisRuleEntity): boolean { + return ( + this.isDisabled(entity) && + this.canRead(entity) && + this.canWrite(entity) && + entity.component.multipleVersionsAvailable === true + ); + } + + canDelete(entity: FlowAnalysisRuleEntity): boolean { + return this.isDisabled(entity) && this.canRead(entity) && this.canWrite(entity) && this.canModifyParent(); + } + + canModifyParent(): boolean { + return this.currentUser.controllerPermissions.canRead && this.currentUser.controllerPermissions.canWrite; + } + + deleteClicked(entity: FlowAnalysisRuleEntity): void { + this.deleteFlowAnalysisRule.next(entity); + } + + canViewState(entity: FlowAnalysisRuleEntity): boolean { + return this.canRead(entity) && this.canWrite(entity) && entity.component.persistsState === true; + } + + select(entity: FlowAnalysisRuleEntity): void { + this.selectFlowAnalysisRule.next(entity); + } + + isSelected(entity: FlowAnalysisRuleEntity): boolean { + if (this.selectedFlowAnalysisRuleId) { + return entity.id == this.selectedFlowAnalysisRuleId; + } + return false; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html index 7f6a64a463..219b7dd22e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.html @@ -15,4 +15,37 @@ ~ limitations under the License. --> -

flow-analysis-rules works!

+ +
+ +
+ +
+
+ +
+
+ +
+
+
+ +
Last updated:
+
{{ flowAnalysisRuleState.loadedTimestamp }}
+
+
+
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts index 76a8dee5b2..2faeeaa9f5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.spec.ts @@ -18,6 +18,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FlowAnalysisRules } from './flow-analysis-rules.component'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialState } from '../../state/flow-analysis-rules/flow-analysis-rules.reducer'; describe('FlowAnalysisRules', () => { let component: FlowAnalysisRules; @@ -25,7 +27,12 @@ describe('FlowAnalysisRules', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [FlowAnalysisRules] + declarations: [FlowAnalysisRules], + providers: [ + provideMockStore({ + initialState + }) + ] }); fixture = TestBed.createComponent(FlowAnalysisRules); component = fixture.componentInstance; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts index 721bf268a5..42f882ceba 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.component.ts @@ -15,11 +15,138 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { Store } from '@ngrx/store'; +import { filter, switchMap, take } from 'rxjs'; +import { + selectFlowAnalysisRuleIdFromRoute, + selectFlowAnalysisRulesState, + selectSingleEditedFlowAnalysisRule, + selectRule +} from '../../state/flow-analysis-rules/flow-analysis-rules.selectors'; +import { + loadFlowAnalysisRules, + navigateToEditFlowAnalysisRule, + openConfigureFlowAnalysisRuleDialog, + openNewFlowAnalysisRuleDialog, + promptFlowAnalysisRuleDeletion, + resetFlowAnalysisRulesState, + selectFlowAnalysisRule, + enableFlowAnalysisRule, + disableFlowAnalysisRule +} from '../../state/flow-analysis-rules/flow-analysis-rules.actions'; +import { initialState } from '../../state/flow-analysis-rules/flow-analysis-rules.reducer'; +import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; +import { NiFiState } from '../../../../state'; +import { FlowAnalysisRuleEntity, FlowAnalysisRulesState } from '../../state/flow-analysis-rules'; +import { CurrentUser } from '../../../../state/current-user'; @Component({ selector: 'flow-analysis-rules', templateUrl: './flow-analysis-rules.component.html', styleUrls: ['./flow-analysis-rules.component.scss'] }) -export class FlowAnalysisRules {} +export class FlowAnalysisRules implements OnInit, OnDestroy { + flowAnalysisRuleState$ = this.store.select(selectFlowAnalysisRulesState); + selectedFlowAnalysisRuleId$ = this.store.select(selectFlowAnalysisRuleIdFromRoute); + currentUser$ = this.store.select(selectCurrentUser); + + constructor(private store: Store) { + this.store + .select(selectSingleEditedFlowAnalysisRule) + .pipe( + filter((id: string) => id != null), + switchMap((id: string) => + this.store.select(selectRule(id)).pipe( + filter((entity) => entity != null), + take(1) + ) + ), + takeUntilDestroyed() + ) + .subscribe((entity) => { + if (entity) { + this.store.dispatch( + openConfigureFlowAnalysisRuleDialog({ + request: { + id: entity.id, + flowAnalysisRule: entity + } + }) + ); + } + }); + } + + ngOnInit(): void { + this.store.dispatch(loadFlowAnalysisRules()); + } + + isInitialLoading(state: FlowAnalysisRulesState): boolean { + // using the current timestamp to detect the initial load event + return state.loadedTimestamp == initialState.loadedTimestamp; + } + + openNewFlowAnalysisRuleDialog(): void { + this.store.dispatch(openNewFlowAnalysisRuleDialog()); + } + + refreshFlowAnalysisRuleListing(): void { + this.store.dispatch(loadFlowAnalysisRules()); + } + + selectFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void { + this.store.dispatch( + selectFlowAnalysisRule({ + request: { + id: entity.id + } + }) + ); + } + + enableFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void { + this.store.dispatch( + enableFlowAnalysisRule({ + request: { + id: entity.id, + flowAnalysisRule: entity + } + }) + ); + } + + disableFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void { + this.store.dispatch( + disableFlowAnalysisRule({ + request: { + id: entity.id, + flowAnalysisRule: entity + } + }) + ); + } + + deleteFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void { + this.store.dispatch( + promptFlowAnalysisRuleDeletion({ + request: { + flowAnalysisRule: entity + } + }) + ); + } + + configureFlowAnalysisRule(entity: FlowAnalysisRuleEntity): void { + this.store.dispatch( + navigateToEditFlowAnalysisRule({ + id: entity.id + }) + ); + } + + ngOnDestroy(): void { + this.store.dispatch(resetFlowAnalysisRulesState()); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts index 69b8ee0c3a..4337832580 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rules.module.ts @@ -18,10 +18,24 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FlowAnalysisRules } from './flow-analysis-rules.component'; +import { FlowAnalysisRuleTable } from './flow-analysis-rule-table/flow-analysis-rule-table.component'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import { MatSortModule } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; +import { NifiTooltipDirective } from '../../../../ui/common/tooltips/nifi-tooltip.directive'; +import { PropertyTable } from '../../../../ui/common/property-table/property-table.component'; @NgModule({ declarations: [FlowAnalysisRules], exports: [FlowAnalysisRules], - imports: [CommonModule] + imports: [ + CommonModule, + NgxSkeletonLoaderModule, + MatSortModule, + MatTableModule, + NifiTooltipDirective, + FlowAnalysisRuleTable, + PropertyTable + ] }) export class FlowAnalysisRulesModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component.ts index 75d6ec9a28..36192b4ed5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component.ts @@ -32,19 +32,18 @@ import { ControllerServiceReferencingComponent, InlineServiceCreationRequest, InlineServiceCreationResponse, - Parameter, - ParameterContextReferenceEntity, Property, - PropertyTipInput, SelectOption, - TextTipInput, - UpdateControllerServiceRequest, - UpdateReportingTaskRequest + TextTipInput } from '../../../../../state/shared'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component'; import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive'; -import { EditReportingTaskDialogRequest, ReportingTaskEntity } from '../../../state/reporting-tasks'; +import { + EditReportingTaskDialogRequest, + ReportingTaskEntity, + UpdateReportingTaskRequest +} from '../../../state/reporting-tasks'; import { ControllerServiceApi } from '../../../../../ui/common/controller-service/controller-service-api/controller-service-api.component'; import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive'; import { TextTip } from '../../../../../ui/common/tooltips/text-tip/text-tip.component'; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html index eba96b9dfa..fd5b0c3062 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html @@ -23,27 +23,33 @@ -
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts index aecd46c816..db29215cb9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts @@ -46,6 +46,11 @@ export const selectRegistryClientTypes = createSelector( (state: ExtensionTypesState) => state.registryClientTypes ); +export const selectFlowAnalysisRuleTypes = createSelector( + selectExtensionTypesState, + (state: ExtensionTypesState) => state.flowAnalysisRuleTypes +); + export const selectTypesToIdentifyComponentRestrictions = createSelector( selectExtensionTypesState, (state: ExtensionTypesState) => { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts index 199e5b03c7..dcd872cace 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts @@ -126,11 +126,6 @@ export interface UpdateControllerServiceRequest { postUpdateNavigation?: string[]; } -export interface UpdateReportingTaskRequest { - payload: any; - postUpdateNavigation?: string[]; -} - export interface SetEnableControllerServiceDialogRequest { id: string; controllerService: ControllerServiceEntity; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html index 4aab0499f9..5508fadd88 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html @@ -31,27 +31,27 @@ because of the gap between items. simple solution is to just wrap the target. --> -
+
-
+
-
+