mirror of https://github.com/apache/nifi.git
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
This commit is contained in:
parent
be16a423ed
commit
f1cac06f2a
|
@ -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<FlowAnalysisRuleEntity> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<any> {
|
||||
return this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`);
|
||||
}
|
||||
|
||||
createFlowAnalysisRule(createFlowAnalysisRule: CreateFlowAnalysisRuleRequest): Observable<any> {
|
||||
return this.httpClient.post(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules`, {
|
||||
revision: createFlowAnalysisRule.revision,
|
||||
component: {
|
||||
bundle: createFlowAnalysisRule.flowAnalysisRuleBundle,
|
||||
type: createFlowAnalysisRule.flowAnalysisRuleType
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteFlowAnalysisRule(deleteFlowAnalysisRule: DeleteFlowAnalysisRuleRequest): Observable<any> {
|
||||
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<any> {
|
||||
const params: any = {
|
||||
propertyName,
|
||||
sensitive
|
||||
};
|
||||
return this.httpClient.get(`${FlowAnalysisRuleService.API}/controller/flow-analysis-rules/${id}/descriptors`, {
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
updateFlowAnalysisRule(configureFlowAnalysisRule: ConfigureFlowAnalysisRuleRequest): Observable<any> {
|
||||
return this.httpClient.put(
|
||||
this.stripProtocol(configureFlowAnalysisRule.uri),
|
||||
configureFlowAnalysisRule.payload
|
||||
);
|
||||
}
|
||||
|
||||
setEnable(flowAnalysisRule: EnableFlowAnalysisRuleRequest, enabled: boolean): Observable<any> {
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 }>()
|
||||
);
|
|
@ -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<NiFiState>,
|
||||
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<Property> => {
|
||||
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<InlineServiceCreationResponse> => {
|
||||
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 }
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
});
|
||||
})
|
||||
);
|
|
@ -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));
|
|
@ -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';
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -58,6 +58,11 @@ export interface ConfigureReportingTaskRequest {
|
|||
postUpdateNavigation?: string[];
|
||||
}
|
||||
|
||||
export interface UpdateReportingTaskRequest {
|
||||
payload: any;
|
||||
postUpdateNavigation?: string[];
|
||||
}
|
||||
|
||||
export interface EditReportingTaskDialogRequest {
|
||||
id: string;
|
||||
reportingTask: ReportingTaskEntity;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<extension-creation
|
||||
[componentType]="'Flow Analysis Rule'"
|
||||
[documentedTypes]="flowAnalysisRules"
|
||||
[saving]="(saving$ | async)!"
|
||||
(extensionTypeSelected)="createFlowAnalysisRule($event)"></extension-creation>
|
|
@ -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.
|
||||
*/
|
|
@ -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<CreateFlowAnalysisRule>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -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<FlowAnalysisRulesState>,
|
||||
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
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Flow Analysis Rule</h2>
|
||||
<form class="flow-analysis-rule-edit-form" [formGroup]="editFlowAnalysisRuleForm">
|
||||
<mat-dialog-content>
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Settings">
|
||||
<div class="tab-content py-4 flex gap-x-4">
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
</mat-form-field>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Id</div>
|
||||
<div class="value">{{ request.flowAnalysisRule.id }}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Type</div>
|
||||
<div class="value">{{ formatType(request.flowAnalysisRule) }}</div>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Bundle</div>
|
||||
<div class="value">{{ formatBundle(request.flowAnalysisRule) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col w-full">
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Enforcement Policy</mat-label>
|
||||
<mat-select formControlName="enforcementPolicy">
|
||||
<mat-option
|
||||
*ngFor="let option of strategies"
|
||||
[value]="option.value"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getPropertyTipData(option)"
|
||||
[delayClose]="false">
|
||||
{{ option.text }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Properties">
|
||||
<div class="tab-content py-4">
|
||||
<property-table
|
||||
formControlName="properties"
|
||||
[createNewProperty]="createNewProperty"
|
||||
[createNewService]="createNewService"
|
||||
[goToService]="goToService">
|
||||
</property-table>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Comments">
|
||||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end" *ngIf="{ value: (saving$ | async)! } as saving">
|
||||
<button color="accent" mat-raised-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editFlowAnalysisRuleForm.dirty || editFlowAnalysisRuleForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-raised-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</form>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<EditFlowAnalysisRule>;
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -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<Property>;
|
||||
@Input() createNewService!: (request: InlineServiceCreationRequest) => Observable<InlineServiceCreationResponse>;
|
||||
@Input() saving$!: Observable<boolean>;
|
||||
@Input() goToService!: (serviceId: string) => void;
|
||||
@Output() editFlowAnalysisRule: EventEmitter<UpdateFlowAnalysisRuleRequest> =
|
||||
new EventEmitter<UpdateFlowAnalysisRuleRequest>();
|
||||
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<div class="relative h-full border">
|
||||
<div class="flow-analysis-rule-table listing-table absolute inset-0 overflow-y-auto">
|
||||
<table
|
||||
mat-table
|
||||
[dataSource]="dataSource"
|
||||
matSort
|
||||
matSortDisableClear
|
||||
(matSortChange)="updateSort($event)"
|
||||
[matSortActive]="sort.active"
|
||||
[matSortDirection]="sort.direction">
|
||||
<!-- More Details Column -->
|
||||
<ng-container matColumnDef="moreDetails">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<ng-container *ngIf="canRead(item)">
|
||||
<div class="flex items-center gap-x-3">
|
||||
<div class="pointer fa fa-book" title="Usage"></div>
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
<div *ngIf="hasComments(item)">
|
||||
<div
|
||||
class="pointer fa fa-comment"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getCommentsTipData(item)"></div>
|
||||
</div>
|
||||
<div *ngIf="hasErrors(item)">
|
||||
<div
|
||||
class="pointer fa fa-warning has-errors"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="ValidationErrorsTip"
|
||||
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
|
||||
</div>
|
||||
<div *ngIf="hasBulletins(item)">
|
||||
<div
|
||||
class="pointer fa fa-sticky-note-o"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="BulletinsTip"
|
||||
[tooltipInputData]="getBulletinsTipData(item)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<ng-container *ngIf="canRead(item); else nameNoPermissions">
|
||||
{{ item.component.name }}
|
||||
</ng-container>
|
||||
<ng-template #nameNoPermissions>
|
||||
<div class="unset">{{ item.id }}</div>
|
||||
</ng-template>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Type Column -->
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Type</th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<ng-container *ngIf="canRead(item)">
|
||||
{{ formatType(item) }}
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Bundle Column -->
|
||||
<ng-container matColumnDef="bundle">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Bundle</th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<ng-container *ngIf="canRead(item)">
|
||||
{{ formatBundle(item) }}
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- State Column -->
|
||||
<ng-container matColumnDef="state">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>State</th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div [ngClass]="getStateIcon(item)"></div>
|
||||
<div>{{ formatState(item) }}</div>
|
||||
<div *ngIf="hasActiveThreads(item)">({{ item.status.activeThreadCount }})</div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center gap-x-3">
|
||||
<div
|
||||
class="pointer fa fa-gear"
|
||||
*ngIf="canConfigure(item)"
|
||||
(click)="configureClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
<div
|
||||
class="pointer fa icon icon-enable-false"
|
||||
*ngIf="canDisable(item)"
|
||||
(click)="disableClicked(item, $event)"
|
||||
title="Disable"></div>
|
||||
<div
|
||||
class="pointer fa fa-flash"
|
||||
*ngIf="canEnable(item)"
|
||||
(click)="enabledClicked(item, $event)"
|
||||
title="Enable"></div>
|
||||
<div class="pointer fa fa-exchange" *ngIf="canChangeVersion(item)" title="Change Version"></div>
|
||||
<div
|
||||
class="pointer fa fa-trash"
|
||||
*ngIf="canDelete(item)"
|
||||
(click)="deleteClicked(item)"
|
||||
title="Delete"></div>
|
||||
<div class="pointer fa fa-tasks" *ngIf="canViewState(item)" title="View State"></div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
|
||||
<tr
|
||||
mat-row
|
||||
*matRowDef="let row; let even = even; columns: displayedColumns"
|
||||
(click)="select(row)"
|
||||
[class.selected]="isSelected(row)"
|
||||
[class.even]="even"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<FlowAnalysisRuleTable>;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [BrowserAnimationsModule, MatTableModule, FlowAnalysisRuleTable]
|
||||
});
|
||||
fixture = TestBed.createComponent(FlowAnalysisRuleTable);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -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<FlowAnalysisRuleEntity> = new EventEmitter<FlowAnalysisRuleEntity>();
|
||||
@Output() deleteFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = new EventEmitter<FlowAnalysisRuleEntity>();
|
||||
@Output() configureFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> =
|
||||
new EventEmitter<FlowAnalysisRuleEntity>();
|
||||
@Output() enableFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> = new EventEmitter<FlowAnalysisRuleEntity>();
|
||||
@Output() disableFlowAnalysisRule: EventEmitter<FlowAnalysisRuleEntity> =
|
||||
new EventEmitter<FlowAnalysisRuleEntity>();
|
||||
|
||||
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<FlowAnalysisRuleEntity> = new MatTableDataSource<FlowAnalysisRuleEntity>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -15,4 +15,37 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<p>flow-analysis-rules works!</p>
|
||||
<ng-container *ngIf="flowAnalysisRuleState$ | async; let flowAnalysisRuleState">
|
||||
<div *ngIf="isInitialLoading(flowAnalysisRuleState); else loaded">
|
||||
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
|
||||
</div>
|
||||
<ng-template #loaded>
|
||||
<div class="flex flex-col h-full gap-y-2" *ngIf="currentUser$ | async; let currentUser">
|
||||
<div class="flex justify-end" *ngIf="currentUser.controllerPermissions.canWrite">
|
||||
<button class="nifi-button" (click)="openNewFlowAnalysisRuleDialog()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<flow-analysis-rule-table
|
||||
[currentUser]="currentUser"
|
||||
[selectedFlowAnalysisRuleId]="selectedFlowAnalysisRuleId$ | async"
|
||||
[flowAnalysisRules]="flowAnalysisRuleState.flowAnalysisRules"
|
||||
(configureFlowAnalysisRule)="configureFlowAnalysisRule($event)"
|
||||
(selectFlowAnalysisRule)="selectFlowAnalysisRule($event)"
|
||||
(enableFlowAnalysisRule)="enableFlowAnalysisRule($event)"
|
||||
(disableFlowAnalysisRule)="disableFlowAnalysisRule($event)"
|
||||
(deleteFlowAnalysisRule)="deleteFlowAnalysisRule($event)"></flow-analysis-rule-table>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<div class="refresh-container flex items-center gap-x-2">
|
||||
<button class="nifi-button" (click)="refreshFlowAnalysisRuleListing()">
|
||||
<i class="fa fa-refresh" [class.fa-spin]="flowAnalysisRuleState.status === 'loading'"></i>
|
||||
</button>
|
||||
<div>Last updated:</div>
|
||||
<div class="refresh-timestamp">{{ flowAnalysisRuleState.loadedTimestamp }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<NiFiState>) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -23,27 +23,33 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<ng-container *ngIf="canRead(item)">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3 pointer fa fa-book" title="Usage"></div>
|
||||
<div class="flex items-center gap-x-3">
|
||||
<div class="pointer fa fa-book" title="Usage"></div>
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
<div
|
||||
class="mr-3 pointer fa fa-comment"
|
||||
*ngIf="hasComments(item)"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getCommentsTipData(item)"></div>
|
||||
<div
|
||||
class="mr-3 pointer fa fa-warning has-errors"
|
||||
*ngIf="hasErrors(item)"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="ValidationErrorsTip"
|
||||
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
|
||||
<div
|
||||
class="mr-3 pointer fa fa-sticky-note-o"
|
||||
*ngIf="hasBulletins(item)"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="BulletinsTip"
|
||||
[tooltipInputData]="getBulletinsTipData(item)"></div>
|
||||
<div *ngIf="hasComments(item)">
|
||||
<div
|
||||
class="pointer fa fa-comment"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getCommentsTipData(item)"></div>
|
||||
</div>
|
||||
<div *ngIf="hasErrors(item)">
|
||||
<div
|
||||
class="pointer fa fa-warning has-errors"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="ValidationErrorsTip"
|
||||
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
|
||||
</div>
|
||||
<div *ngIf="hasBulletins(item)">
|
||||
<div
|
||||
class="pointer fa fa-sticky-note-o"
|
||||
[delayClose]="false"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="BulletinsTip"
|
||||
[tooltipInputData]="getBulletinsTipData(item)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</td>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -126,11 +126,6 @@ export interface UpdateControllerServiceRequest {
|
|||
postUpdateNavigation?: string[];
|
||||
}
|
||||
|
||||
export interface UpdateReportingTaskRequest {
|
||||
payload: any;
|
||||
postUpdateNavigation?: string[];
|
||||
}
|
||||
|
||||
export interface SetEnableControllerServiceDialogRequest {
|
||||
id: string;
|
||||
controllerService: ControllerServiceEntity;
|
||||
|
|
|
@ -31,27 +31,27 @@
|
|||
because of the gap between items. simple solution is to
|
||||
just wrap the target.
|
||||
-->
|
||||
<div>
|
||||
<div *ngIf="hasComments(item)">
|
||||
<div
|
||||
class="pointer fa fa-comment"
|
||||
*ngIf="hasComments(item)"
|
||||
nifiTooltip
|
||||
[delayClose]="false"
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getCommentsTipData(item)"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div *ngIf="hasErrors(item)">
|
||||
<div
|
||||
class="pointer fa fa-warning has-errors"
|
||||
*ngIf="hasErrors(item)"
|
||||
nifiTooltip
|
||||
[delayClose]="false"
|
||||
[tooltipComponentType]="ValidationErrorsTip"
|
||||
[tooltipInputData]="getValidationErrorsTipData(item)"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div *ngIf="hasBulletins(item)">
|
||||
<div
|
||||
class="pointer fa fa-sticky-note-o"
|
||||
*ngIf="hasBulletins(item)"
|
||||
nifiTooltip
|
||||
[delayClose]="false"
|
||||
[tooltipComponentType]="BulletinsTip"
|
||||
[tooltipInputData]="getBulletinsTipData(item)"></div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue