NIFI-12948: (#8564)

- Action is optional.
- Removing duplicate check in counter table.
- Ensure flow configuration is loaded in route guard.
- Only show user/group list when there are users or groups.
- Default override policy to copy.
This commit is contained in:
Matt Gilman 2024-03-26 15:21:11 -04:00 committed by GitHub
parent 339a06a8c6
commit 0c01dfe585
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 240 additions and 218 deletions

View File

@ -35,7 +35,7 @@ export class OverridePolicyDialog {
constructor(private formBuilder: FormBuilder) { constructor(private formBuilder: FormBuilder) {
this.overridePolicyForm = this.formBuilder.group({ this.overridePolicyForm = this.formBuilder.group({
override: new FormControl() override: new FormControl('copy')
}); });
} }

View File

@ -74,7 +74,6 @@
<ng-container matColumnDef="reset"> <ng-container matColumnDef="reset">
<th mat-header-cell *matHeaderCellDef></th> <th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let item"> <td mat-cell *matCellDef="let item">
@if (canModifyCounters) {
<div class="flex items-center gap-x-3"> <div class="flex items-center gap-x-3">
@if (canModifyCounters) { @if (canModifyCounters) {
<div <div
@ -83,7 +82,6 @@
(click)="resetClicked(item, $event)"></div> (click)="resetClicked(item, $event)"></div>
} }
</div> </div>
}
</td> </td>
</ng-container> </ng-container>

View File

@ -33,7 +33,7 @@ export interface ActionEntity {
timestamp: string; timestamp: string;
sourceId: string; sourceId: string;
canRead: boolean; canRead: boolean;
action: Action; action?: Action;
} }
export interface Action { export interface Action {

View File

@ -25,14 +25,15 @@
<div class="value">{{ actionEntity.sourceId }}</div> <div class="value">{{ actionEntity.sourceId }}</div>
</div> </div>
@if (actionEntity.action.componentDetails) { @if (actionEntity.action; as action) {
@if (isRemoteProcessGroup(actionEntity)) { @if (action.componentDetails) {
@if (isRemoteProcessGroup(action)) {
<div> <div>
<div>Uri</div> <div>Uri</div>
<ng-container <ng-container
*ngTemplateOutlet=" *ngTemplateOutlet="
formatValue; formatValue;
context: { $implicit: getRemoteProcessGroupDetails(actionEntity)?.uri } context: { $implicit: getRemoteProcessGroupDetails(action)?.uri }
"></ng-container> "></ng-container>
</div> </div>
} @else { } @else {
@ -41,16 +42,16 @@
<ng-container <ng-container
*ngTemplateOutlet=" *ngTemplateOutlet="
formatValue; formatValue;
context: { $implicit: getExtensionDetails(actionEntity)?.type } context: { $implicit: getExtensionDetails(action)?.type }
"></ng-container> "></ng-container>
</div> </div>
} }
} }
@if (actionEntity.action.actionDetails) { @if (action.actionDetails) {
@switch (actionEntity.action.operation) { @switch (action.operation) {
@case ('Configure') { @case ('Configure') {
@if (getConfigureActionDetails(actionEntity); as details) { @if (getConfigureActionDetails(action); as details) {
<div> <div>
<div>Name</div> <div>Name</div>
<ng-container <ng-container
@ -78,7 +79,7 @@
} }
} }
@case ('Connect' || 'Disconnect') { @case ('Connect' || 'Disconnect') {
@if (getConnectActionDetails(actionEntity); as details) { @if (getConnectActionDetails(action); as details) {
<div> <div>
<div>Source Id</div> <div>Source Id</div>
<ng-container <ng-container
@ -138,7 +139,7 @@
} }
} }
@case ('Move') { @case ('Move') {
@if (getMoveActionDetails(actionEntity); as details) { @if (getMoveActionDetails(action); as details) {
<div> <div>
<div>Group</div> <div>Group</div>
<ng-container <ng-container
@ -174,7 +175,7 @@
} }
} }
@case ('Purge') { @case ('Purge') {
@if (getPurgeActionDetails(actionEntity); as details) { @if (getPurgeActionDetails(action); as details) {
<div> <div>
<div>End Date</div> <div>End Date</div>
<ng-container <ng-container
@ -187,6 +188,7 @@
} }
} }
} }
}
</div> </div>
<ng-template #formatValue let-value let-title="title"> <ng-template #formatValue let-value let-title="title">

View File

@ -19,6 +19,7 @@ import { Component, Inject } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { import {
Action,
ActionEntity, ActionEntity,
ConfigureActionDetails, ConfigureActionDetails,
ConnectionActionDetails, ConnectionActionDetails,
@ -27,7 +28,6 @@ import {
PurgeActionDetails, PurgeActionDetails,
RemoteProcessGroupDetails RemoteProcessGroupDetails
} from '../../../state/flow-configuration-history-listing'; } from '../../../state/flow-configuration-history-listing';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
import { PipesModule } from '../../../../../pipes/pipes.module'; import { PipesModule } from '../../../../../pipes/pipes.module';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@ -39,54 +39,51 @@ import { MatButtonModule } from '@angular/material/button';
styleUrls: ['./action-details.component.scss'] styleUrls: ['./action-details.component.scss']
}) })
export class ActionDetails { export class ActionDetails {
constructor( constructor(@Inject(MAT_DIALOG_DATA) public actionEntity: ActionEntity) {}
@Inject(MAT_DIALOG_DATA) public actionEntity: ActionEntity,
private nifiCommon: NiFiCommon
) {}
isRemoteProcessGroup(actionEntity: ActionEntity): boolean { isRemoteProcessGroup(action: Action): boolean {
return actionEntity.action.sourceType === 'RemoteProcessGroup'; return action.sourceType === 'RemoteProcessGroup';
} }
getRemoteProcessGroupDetails(actionEntity: ActionEntity): RemoteProcessGroupDetails | null { getRemoteProcessGroupDetails(action: Action): RemoteProcessGroupDetails | null {
if (!this.isRemoteProcessGroup(actionEntity)) { if (!this.isRemoteProcessGroup(action)) {
return null; return null;
} }
return actionEntity.action.componentDetails as RemoteProcessGroupDetails; return action.componentDetails as RemoteProcessGroupDetails;
} }
getExtensionDetails(actionEntity: ActionEntity): ExtensionDetails | null { getExtensionDetails(action: Action): ExtensionDetails | null {
if (this.isRemoteProcessGroup(actionEntity)) { if (this.isRemoteProcessGroup(action)) {
return null; return null;
} }
return actionEntity.action.componentDetails as ExtensionDetails; return action.componentDetails as ExtensionDetails;
} }
getConfigureActionDetails(actionEntity: ActionEntity): ConfigureActionDetails | null { getConfigureActionDetails(action: Action): ConfigureActionDetails | null {
if (actionEntity.action.operation !== 'Configure') { if (action.operation !== 'Configure') {
return null; return null;
} }
return actionEntity.action.actionDetails as ConfigureActionDetails; return action.actionDetails as ConfigureActionDetails;
} }
getConnectActionDetails(actionEntity: ActionEntity): ConnectionActionDetails | null { getConnectActionDetails(action: Action): ConnectionActionDetails | null {
if (!['Connect', 'Disconnect'].includes(actionEntity.action.operation)) { if (!['Connect', 'Disconnect'].includes(action.operation)) {
return null; return null;
} }
return actionEntity.action.actionDetails as ConnectionActionDetails; return action.actionDetails as ConnectionActionDetails;
} }
getMoveActionDetails(actionEntity: ActionEntity): MoveActionDetails | null { getMoveActionDetails(action: Action): MoveActionDetails | null {
if (actionEntity.action.operation !== 'Move') { if (action.operation !== 'Move') {
return null; return null;
} }
return actionEntity.action.actionDetails as MoveActionDetails; return action.actionDetails as MoveActionDetails;
} }
getPurgeActionDetails(actionEntity: ActionEntity): PurgeActionDetails | null { getPurgeActionDetails(action: Action): PurgeActionDetails | null {
if (actionEntity.action.operation !== 'Purge') { if (action.operation !== 'Purge') {
return null; return null;
} }
return actionEntity.action.actionDetails as PurgeActionDetails; return action.actionDetails as PurgeActionDetails;
} }
} }

View File

@ -20,7 +20,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatSortModule, Sort } from '@angular/material/sort'; import { MatSortModule, Sort } from '@angular/material/sort';
import { ActionEntity } from '../../../state/flow-configuration-history-listing'; import { ActionEntity } from '../../../state/flow-configuration-history-listing';
import { NiFiCommon } from '../../../../../service/nifi-common.service';
@Component({ @Component({
selector: 'flow-configuration-history-table', selector: 'flow-configuration-history-table',
@ -52,7 +51,7 @@ export class FlowConfigurationHistoryTable {
displayedColumns: string[] = ['moreDetails', 'timestamp', 'sourceName', 'sourceType', 'operation', 'userIdentity']; displayedColumns: string[] = ['moreDetails', 'timestamp', 'sourceName', 'sourceType', 'operation', 'userIdentity'];
dataSource: MatTableDataSource<ActionEntity> = new MatTableDataSource<ActionEntity>(); dataSource: MatTableDataSource<ActionEntity> = new MatTableDataSource<ActionEntity>();
constructor(private nifiCommon: NiFiCommon) {} constructor() {}
sortData(sort: Sort) { sortData(sort: Sort) {
this.sortChanged.next(sort); this.sortChanged.next(sort);
@ -63,7 +62,7 @@ export class FlowConfigurationHistoryTable {
} }
private format(item: ActionEntity, property: string): string { private format(item: ActionEntity, property: string): string {
if (this.canRead(item) && Object.hasOwn(item.action, property)) { if (this.canRead(item) && item.action && Object.hasOwn(item.action, property)) {
const value = (item.action as any)[property]; const value = (item.action as any)[property];
if (!value) { if (!value) {
return 'Empty String Set'; return 'Empty String Set';

View File

@ -17,10 +17,12 @@
import { CanMatchFn, Router } from '@angular/router'; import { CanMatchFn, Router } from '@angular/router';
import { inject } from '@angular/core'; import { inject } from '@angular/core';
import { map } from 'rxjs'; import { catchError, map, of, switchMap, tap } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { FlowConfiguration, FlowConfigurationState } from '../../state/flow-configuration'; import { FlowConfiguration, FlowConfigurationState } from '../../state/flow-configuration';
import { selectFlowConfiguration } from '../../state/flow-configuration/flow-configuration.selectors'; import { selectFlowConfiguration } from '../../state/flow-configuration/flow-configuration.selectors';
import { FlowConfigurationService } from '../flow-configuration.service';
import { loadFlowConfigurationSuccess } from '../../state/flow-configuration/flow-configuration.actions';
export const checkFlowConfiguration = ( export const checkFlowConfiguration = (
flowConfigurationCheck: (flowConfiguration: FlowConfiguration) => boolean flowConfigurationCheck: (flowConfiguration: FlowConfiguration) => boolean
@ -28,15 +30,35 @@ export const checkFlowConfiguration = (
return () => { return () => {
const router: Router = inject(Router); const router: Router = inject(Router);
const store: Store<FlowConfigurationState> = inject(Store<FlowConfigurationState>); const store: Store<FlowConfigurationState> = inject(Store<FlowConfigurationState>);
const flowConfigurationService: FlowConfigurationService = inject(FlowConfigurationService);
return store.select(selectFlowConfiguration).pipe( return store.select(selectFlowConfiguration).pipe(
switchMap((flowConfiguration) => {
if (flowConfiguration) {
return of(flowConfiguration);
} else {
return flowConfigurationService.getFlowConfiguration().pipe(
tap((response) =>
store.dispatch(
loadFlowConfigurationSuccess({
response
})
)
)
);
}
}),
map((flowConfiguration) => { map((flowConfiguration) => {
if (flowConfiguration && flowConfigurationCheck(flowConfiguration)) { if (flowConfigurationCheck(flowConfiguration)) {
return true; return true;
} }
// TODO - replace with 409 error page // TODO - replace with error page
return router.parseUrl('/'); return router.parseUrl('/');
}),
catchError(() => {
// TODO - replace with error page
return of(router.parseUrl('/'));
}) })
); );
}; };

View File

@ -19,13 +19,13 @@
<form class="edit-tenant-form" [formGroup]="editTenantForm"> <form class="edit-tenant-form" [formGroup]="editTenantForm">
<error-banner></error-banner> <error-banner></error-banner>
<mat-dialog-content> <mat-dialog-content>
<div class="mb-6"> <div>
<mat-radio-group formControlName="tenantType" (change)="tenantTypeChanged()"> <mat-radio-group formControlName="tenantType" (change)="tenantTypeChanged()">
<mat-radio-button color="primary" [value]="USER">Individual</mat-radio-button> <mat-radio-button color="primary" [value]="USER">Individual</mat-radio-button>
<mat-radio-button color="primary" [value]="USER_GROUP">Group</mat-radio-button> <mat-radio-button color="primary" [value]="USER_GROUP">Group</mat-radio-button>
</mat-radio-group> </mat-radio-group>
</div> </div>
<div class="mb-2"> <div class="mt-6">
<mat-form-field> <mat-form-field>
<mat-label>Identity</mat-label> <mat-label>Identity</mat-label>
<input matInput formControlName="identity" type="text" /> <input matInput formControlName="identity" type="text" />
@ -35,7 +35,8 @@
</mat-form-field> </mat-form-field>
</div> </div>
@if (isUser) { @if (isUser) {
<div> @if (userGroups.length > 0) {
<div class="mt-2">
<mat-label>Member of</mat-label> <mat-label>Member of</mat-label>
<mat-selection-list formControlName="userGroups" class="border"> <mat-selection-list formControlName="userGroups" class="border">
@for (userGroup of userGroups; track userGroup) { @for (userGroup of userGroups; track userGroup) {
@ -45,8 +46,10 @@
} }
</mat-selection-list> </mat-selection-list>
</div> </div>
}
} @else { } @else {
<div> @if (users.length > 0) {
<div class="mt-2">
<mat-label>Members</mat-label> <mat-label>Members</mat-label>
<mat-selection-list formControlName="users" class="border"> <mat-selection-list formControlName="users" class="border">
@for (user of users; track user) { @for (user of users; track user) {
@ -57,6 +60,7 @@
</mat-selection-list> </mat-selection-list>
</div> </div>
} }
}
</mat-dialog-content> </mat-dialog-content>
@if ({ value: (saving$ | async)! }; as saving) { @if ({ value: (saving$ | async)! }; as saving) {
<mat-dialog-actions align="end"> <mat-dialog-actions align="end">