From f1cac06f2aa40a3b4f5f3e9415500cc007df37ee Mon Sep 17 00:00:00 2001 From: Scott Aslan Date: Fri, 12 Jan 2024 17:15:23 -0500 Subject: [PATCH] NIFI-12588: Flow Analysis Rules (#8241) * [NIFI-12588] Flow Analysis Rules listing review feedback update goto dialog title remove transitional states of enable fix CS goto remove access policy UX check canModifyController update currentTime move interfaces update sorting, inline CS creation, CS goto use Edit in dialog titles * review feedback * input current user This closes #8241 --- .../api/entity/FlowAnalysisRulesEntity.java | 22 + .../nifi/web/api/ControllerResource.java | 1 + .../flow-designer/state/flow/flow.effects.ts | 12 - .../feature/settings-routing.module.ts | 17 +- .../pages/settings/feature/settings.module.ts | 2 + .../service/flow-analysis-rule.service.ts | 99 ++++ .../flow-analysis-rules.actions.ts | 123 ++++ .../flow-analysis-rules.effects.ts | 524 ++++++++++++++++++ .../flow-analysis-rules.reducer.ts | 123 ++++ .../flow-analysis-rules.selectors.ts | 54 ++ .../state/flow-analysis-rules/index.ts | 122 ++++ .../src/app/pages/settings/state/index.ts | 4 + .../settings/state/reporting-tasks/index.ts | 5 + .../reporting-tasks.effects.ts | 14 +- .../create-flow-analysis-rule.component.html | 22 + .../create-flow-analysis-rule.component.scss | 16 + ...reate-flow-analysis-rule.component.spec.ts | 61 ++ .../create-flow-analysis-rule.component.ts | 62 +++ .../edit-flow-analysis-rule.component.html | 93 ++++ .../edit-flow-analysis-rule.component.scss | 43 ++ .../edit-flow-analysis-rule.component.spec.ts | 105 ++++ .../edit-flow-analysis-rule.component.ts | 161 ++++++ .../flow-analysis-rule-table.component.html | 151 +++++ .../flow-analysis-rule-table.component.scss | 24 + ...flow-analysis-rule-table.component.spec.ts | 40 ++ .../flow-analysis-rule-table.component.ts | 268 +++++++++ .../flow-analysis-rules.component.html | 35 +- .../flow-analysis-rules.component.spec.ts | 9 +- .../flow-analysis-rules.component.ts | 131 ++++- .../flow-analysis-rules.module.ts | 16 +- .../edit-reporting-task.component.ts | 13 +- .../reporting-task-table.component.html | 46 +- .../extension-types.selectors.ts | 5 + .../main/nifi/src/app/state/shared/index.ts | 5 - .../controller-service-table.component.html | 12 +- 35 files changed, 2371 insertions(+), 69 deletions(-) create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 create mode 100644 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 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. --> -
+
-
+
-
+