From 455159f6ace9c3902937468344a50ad9594d03ca Mon Sep 17 00:00:00 2001 From: Rob Fellows Date: Thu, 29 Feb 2024 16:08:47 -0500 Subject: [PATCH] [NIFI-12537] Open cluster/node dialog from Summary screen. (#8454) * [NIFI-12537] - Summary screen cluster-related features * common component status table * cluster summary dialogs * address review feedback * ellisis for node columns in cluster dialogs, cluster node dropdown panel no longer wraps, shorted the dropdown width as well. * prettier This closes #8454 --- .../summary/feature/summary.component.html | 14 +- .../summary/feature/summary.component.ts | 20 +- .../pages/summary/feature/summary.module.ts | 3 +- .../component-cluster-status.service.ts | 64 +++++ .../service/process-group-status.service.ts | 18 +- .../component-cluster-status.actions.ts | 43 ++++ .../component-cluster-status.effects.ts | 114 +++++++++ .../component-cluster-status.reducer.ts | 80 ++++++ .../component-cluster-status.selectors.ts | 45 ++++ .../state/component-cluster-status/index.ts | 47 ++++ .../nifi/src/app/pages/summary/state/index.ts | 225 ++++++++++++++++- .../summary/state/summary-listing/index.ts | 165 ++---------- .../summary-listing.actions.ts | 6 + .../summary-listing.effects.ts | 33 +-- .../summary-listing.reducer.ts | 21 +- .../summary-listing.selectors.ts | 18 +- .../cluster-summary-dialog.component.html | 85 +++++++ .../cluster-summary-dialog.component.scss} | 28 ++- .../cluster-summary-dialog.component.spec.ts | 62 +++++ .../cluster-summary-dialog.component.ts | 140 +++++++++++ .../component-cluster-table.component.ts | 120 +++++++++ .../connection-cluster-table.component.html | 130 ++++++++++ .../connection-cluster-table.component.scss | 25 ++ ...connection-cluster-table.component.spec.ts | 40 +++ .../connection-cluster-table.component.ts | 109 ++++++++ .../port-cluster-table.component.html | 107 ++++++++ .../port-cluster-table.component.scss | 25 ++ .../port-cluster-table.component.spec.ts | 40 +++ .../port-cluster-table.component.ts | 127 ++++++++++ ...process-group-cluster-table.component.html | 172 +++++++++++++ ...process-group-cluster-table.component.scss | 25 ++ ...cess-group-cluster-table.component.spec.ts | 40 +++ .../process-group-cluster-table.component.ts | 144 +++++++++++ .../processor-cluster-table.component.html | 151 +++++++++++ .../processor-cluster-table.component.scss | 25 ++ .../processor-cluster-table.component.spec.ts | 40 +++ .../processor-cluster-table.component.ts | 138 ++++++++++ ...process-group-cluster-table.component.html | 110 ++++++++ ...process-group-cluster-table.component.scss | 25 ++ ...cess-group-cluster-table.component.spec.ts | 40 +++ ...e-process-group-cluster-table.component.ts | 120 +++++++++ .../component-status-table.component.ts | 236 ++++++++++++++++++ .../port-status-table.component.html | 10 +- .../port-status-table.component.scss | 4 +- .../port-status-table.component.ts | 166 ++---------- .../summary-table-filter.component.html | 29 ++- .../summary-table-filter.component.scss | 10 + .../summary-table-filter.component.ts | 118 ++++++++- .../connection-status-listing.component.html | 11 +- .../connection-status-listing.component.ts | 40 ++- .../connection-status-table.component.html | 9 + .../connection-status-table.component.scss | 4 +- .../connection-status-table.component.ts | 170 ++----------- .../input-port-status-listing.component.html | 11 +- .../input-port-status-listing.component.ts | 38 ++- .../output-port-status-listing.component.html | 11 +- .../output-port-status-listing.component.ts | 38 ++- ...rocess-group-status-listing.component.html | 11 +- .../process-group-status-listing.component.ts | 40 ++- .../process-group-status-listing.module.ts | 3 +- .../process-group-status-table.component.html | 9 + .../process-group-status-table.component.scss | 4 +- .../process-group-status-table.component.ts | 166 ++---------- .../processor-status-listing.component.html | 11 +- .../processor-status-listing.component.ts | 40 ++- ...rocessor-status-table.component-theme.scss | 42 ++++ .../processor-status-table.component.html | 20 +- .../processor-status-table.component.scss | 15 +- .../processor-status-table.component.ts | 184 ++------------ ...rocess-group-status-listing.component.html | 11 +- ...-process-group-status-listing.component.ts | 40 ++- ...-process-group-status-table.component.html | 9 + ...-process-group-status-table.component.scss | 4 +- ...te-process-group-status-table.component.ts | 170 ++----------- .../pipes/component-type-name.pipe.spec.ts | 25 ++ .../src/app/pipes/component-type-name.pipe.ts | 60 +++++ .../cluster-summary.actions.ts | 12 +- .../cluster-summary.effects.ts | 38 ++- .../cluster-summary.reducer.ts | 10 +- .../cluster-summary.selectors.ts | 5 + .../src/app/state/cluster-summary/index.ts | 5 + .../_component-context.component-theme.scss | 62 +++++ .../component-context.component.html | 31 +++ .../component-context.component.scss | 41 +++ .../component-context.component.spec.ts | 39 +++ .../component-context.component.ts | 67 +++++ .../assets/styles/_listing-table-theme.scss | 1 + .../src/main/nifi/src/styles.scss | 6 + 88 files changed, 4058 insertions(+), 1012 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/service/component-cluster-status.service.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/state/component-cluster-status/component-cluster-status.actions.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/state/component-cluster-status/component-cluster-status.effects.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/state/component-cluster-status/component-cluster-status.reducer.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/state/component-cluster-status/component-cluster-status.selectors.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/state/component-cluster-status/index.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/cluster-summary-dialog.component.html rename nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/{service/cluster-summary.service.ts => ui/common/cluster-summary-dialog/cluster-summary-dialog.component.scss} (64%) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/cluster-summary-dialog.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/cluster-summary-dialog.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/component-cluster-table/component-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/connection-cluster-table/connection-cluster-table.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/connection-cluster-table/connection-cluster-table.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/connection-cluster-table/connection-cluster-table.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/connection-cluster-table/connection-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/port-cluster-table/port-cluster-table.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/port-cluster-table/port-cluster-table.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/port-cluster-table/port-cluster-table.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/port-cluster-table/port-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/process-group-cluster-table/process-group-cluster-table.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/process-group-cluster-table/process-group-cluster-table.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/process-group-cluster-table/process-group-cluster-table.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/process-group-cluster-table/process-group-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/processor-cluster-table/processor-cluster-table.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/processor-cluster-table/processor-cluster-table.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/processor-cluster-table/processor-cluster-table.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/processor-cluster-table/processor-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/remote-process-group-cluster-table/remote-process-group-cluster-table.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/remote-process-group-cluster-table/remote-process-group-cluster-table.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/remote-process-group-cluster-table/remote-process-group-cluster-table.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/cluster-summary-dialog/remote-process-group-cluster-table/remote-process-group-cluster-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/component-status-table/component-status-table.component.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/_processor-status-table.component-theme.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/_component-context.component-theme.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.html create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.scss create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.spec.ts create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.ts diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/feature/summary.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/feature/summary.component.html index 04196c4e49..02c166050f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/feature/summary.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/feature/summary.component.html @@ -19,8 +19,18 @@
-
-

NiFi Summary

+
+

+ @if (selectedClusterNode$ | async; as selectedNode) { + @if (selectedNode.id !== 'All') { + {{ selectedNode.address }} Summary + } @else { + NiFi Summary + } + } @else { + NiFi Summary + } +

+ @if (connectedToCluster) { +
+ }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.scss index 2a30318aef..4dbbdf8978 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.scss @@ -23,8 +23,8 @@ } .mat-column-actions { - width: 72px; - min-width: 72px; + width: 80px; + min-width: 80px; } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.ts index c3dcf9989c..e42698c244 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/port-status-table/port-status-table.component.ts @@ -15,21 +15,18 @@ * limitations under the License. */ -import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; -import { MultiSort } from '../index'; -import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { PortStatusSnapshot, PortStatusSnapshotEntity } from '../../../state/summary-listing'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; import { SummaryTableFilterModule } from '../summary-table-filter/summary-table-filter.module'; -import { - SummaryTableFilterArgs, - SummaryTableFilterColumn -} from '../summary-table-filter/summary-table-filter.component'; +import { SummaryTableFilterColumn } from '../summary-table-filter/summary-table-filter.component'; import { ComponentType } from '../../../../../state/shared'; import { RouterLink } from '@angular/router'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { PortStatusSnapshot, PortStatusSnapshotEntity } from '../../../state'; +import { ComponentStatusTable } from '../component-status-table/component-status-table.component'; export type SupportedColumns = 'name' | 'runStatus' | 'in' | 'out'; @@ -40,40 +37,22 @@ export type SupportedColumns = 'name' | 'runStatus' | 'in' | 'out'; templateUrl: './port-status-table.component.html', styleUrls: ['./port-status-table.component.scss'] }) -export class PortStatusTable implements AfterViewInit { - private _initialSortColumn: SupportedColumns = 'name'; - private _initialSortDirection: SortDirection = 'asc'; +export class PortStatusTable extends ComponentStatusTable { private _portType!: 'input' | 'output'; filterableColumns: SummaryTableFilterColumn[] = [{ key: 'name', label: 'name' }]; - totalCount = 0; - filteredCount = 0; - - multiSort: MultiSort = { - active: this._initialSortColumn, - direction: this._initialSortDirection, - sortValueIndex: 0, - totalValues: 2 - }; - displayedColumns: string[] = []; - dataSource: MatTableDataSource = new MatTableDataSource(); - - @ViewChild(MatPaginator) paginator!: MatPaginator; - - constructor(private nifiCommon: NiFiCommon) {} - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; + constructor(private nifiCommon: NiFiCommon) { + super(); } @Input() set portType(type: 'input' | 'output') { if (type === 'input') { - this.displayedColumns = ['moreDetails', 'name', 'runStatus', 'in', 'actions']; - } else { this.displayedColumns = ['moreDetails', 'name', 'runStatus', 'out', 'actions']; + } else { + this.displayedColumns = ['moreDetails', 'name', 'runStatus', 'in', 'actions']; } this._portType = type; } @@ -82,74 +61,21 @@ export class PortStatusTable implements AfterViewInit { return this._portType; } - @Input() selectedPortId!: string; + override filterPredicate(data: PortStatusSnapshotEntity, filter: string): boolean { + const { filterTerm, filterColumn, filterStatus } = JSON.parse(filter); + const matchOnStatus: boolean = filterStatus !== 'All'; - @Input() set initialSortColumn(initialSortColumn: SupportedColumns) { - this._initialSortColumn = initialSortColumn; - this.multiSort = { ...this.multiSort, active: initialSortColumn }; - } - - get initialSortColumn() { - return this._initialSortColumn; - } - - @Input() set initialSortDirection(initialSortDirection: SortDirection) { - this._initialSortDirection = initialSortDirection; - this.multiSort = { ...this.multiSort, direction: initialSortDirection }; - } - - get initialSortDirection() { - return this._initialSortDirection; - } - - @Input() set ports(ports: PortStatusSnapshotEntity[]) { - if (ports) { - this.dataSource.data = this.sortEntities(ports, this.multiSort); - this.dataSource.filterPredicate = (data: PortStatusSnapshotEntity, filter: string) => { - const { filterTerm, filterColumn, filterStatus } = JSON.parse(filter); - const matchOnStatus: boolean = filterStatus !== 'All'; - - if (matchOnStatus) { - if (data.portStatusSnapshot.runStatus !== filterStatus) { - return false; - } - } - if (filterTerm === '') { - return true; - } - - const field: string = data.portStatusSnapshot[filterColumn as keyof PortStatusSnapshot] as string; - return this.nifiCommon.stringContains(field, filterTerm, true); - }; - - this.totalCount = ports.length; - this.filteredCount = ports.length; + if (matchOnStatus) { + if (data.portStatusSnapshot.runStatus !== filterStatus) { + return false; + } } - } - - @Input() summaryListingStatus: string | null = null; - @Input() loadedTimestamp: string | null = null; - - @Output() refresh: EventEmitter = new EventEmitter(); - @Output() selectPort: EventEmitter = new EventEmitter(); - @Output() clearSelection: EventEmitter = new EventEmitter(); - - applyFilter(filter: SummaryTableFilterArgs) { - this.dataSource.filter = JSON.stringify(filter); - this.filteredCount = this.dataSource.filteredData.length; - this.resetPaginator(); - this.selectNone(); - } - - resetPaginator(): void { - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); + if (filterTerm === '') { + return true; } - } - paginationChanged(): void { - // clear out any selection - this.selectNone(); + const field: string = data.portStatusSnapshot[filterColumn as keyof PortStatusSnapshot] as string; + return this.nifiCommon.stringContains(field, filterTerm, true); } formatName(port: PortStatusSnapshotEntity): string { @@ -193,31 +119,11 @@ export class PortStatusTable implements AfterViewInit { return ['/process-groups', port.portStatusSnapshot.groupId, componentType, port.id]; } - select(port: PortStatusSnapshotEntity): void { - this.selectPort.next(port); - } - - private selectNone() { - this.clearSelection.next(); - } - - isSelected(port: PortStatusSnapshotEntity): boolean { - if (this.selectedPortId) { - return port.id === this.selectedPortId; - } - return false; - } - - sortData(sort: Sort) { - this.setMultiSort(sort); - this.dataSource.data = this.sortEntities(this.dataSource.data, sort); - } - canRead(port: PortStatusSnapshotEntity) { return port.canRead; } - private supportsMultiValuedSort(sort: Sort): boolean { + override supportsMultiValuedSort(sort: Sort): boolean { switch (sort.active) { case 'in': case 'out': @@ -227,29 +133,7 @@ export class PortStatusTable implements AfterViewInit { } } - private setMultiSort(sort: Sort) { - const { active, direction, sortValueIndex, totalValues } = this.multiSort; - - if (this.supportsMultiValuedSort(sort)) { - if (active === sort.active) { - // previous sort was of the same column - if (direction === 'desc' && sort.direction === 'asc') { - // change from previous index to the next - const newIndex = sortValueIndex + 1 >= totalValues ? 0 : sortValueIndex + 1; - this.multiSort = { ...sort, sortValueIndex: newIndex, totalValues }; - } else { - this.multiSort = { ...sort, sortValueIndex, totalValues }; - } - } else { - // sorting a different column, just reset - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } else { - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } - - private sortEntities(data: PortStatusSnapshotEntity[], sort: Sort): PortStatusSnapshotEntity[] { + override sortEntities(data: PortStatusSnapshotEntity[], sort: Sort): PortStatusSnapshotEntity[] { if (!data) { return []; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.html index e2d5da3aa9..9fc10276ef 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.html @@ -32,7 +32,7 @@ Filter By @for (option of filterableColumns; track option) { - {{ option.label }} + {{ option.label }} } @@ -42,12 +42,12 @@ Status - All Statuses - Running - Stopped - Validating - Disabled - Invalid + All Statuses + Running + Stopped + Validating + Disabled + Invalid
@@ -55,7 +55,20 @@ @if (includePrimaryNodeOnlyFilter) {
- Primary Node + Primary Node +
+ } + + @if (clusterNodes && clusterNodes.length > 0) { +
+ + Cluster Node + + @for (node of clusterNodes; track node) { + {{ node.address }} + } + +
} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.scss index 3651a8ab70..2d81644587 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.scss @@ -14,3 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +.summary-table-filter-container { + .cluster-node-selection { + max-width: 100%; + + .mat-mdc-form-field { + width: 300px; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.ts index 7f46f7a538..f09115e6ad 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/common/summary-table-filter/summary-table-filter.component.ts @@ -19,6 +19,7 @@ import { AfterViewInit, Component, DestroyRef, EventEmitter, inject, Input, Outp import { FormBuilder, FormGroup } from '@angular/forms'; import { debounceTime } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { NodeSearchResult } from '../../../../../state/cluster-summary'; export interface SummaryTableFilterColumn { key: string; @@ -30,6 +31,11 @@ export interface SummaryTableFilterArgs { filterColumn: string; filterStatus?: string; primaryOnly?: boolean; + clusterNode?: NodeSearchResult; +} + +export interface SummaryTableFilterContext extends SummaryTableFilterArgs { + changedField: string; } @Component({ @@ -42,13 +48,71 @@ export class SummaryTableFilter implements AfterViewInit { private _filteredCount = 0; private _totalCount = 0; private _initialFilterColumn = 'name'; + private _filterableColumns: SummaryTableFilterColumn[] = []; private destroyRef: DestroyRef = inject(DestroyRef); - showFilterMatchedLabel = false; - @Input() filterableColumns: SummaryTableFilterColumn[] = []; + showFilterMatchedLabel = false; + allNodes: NodeSearchResult = { + id: 'All', + address: 'All Nodes' + }; + + private _clusterNodes: NodeSearchResult[] = []; + private _selectedNode: NodeSearchResult | null = this.allNodes; + + @Input() set filterableColumns(filterableColumns: SummaryTableFilterColumn[]) { + this._filterableColumns = filterableColumns; + } + get filterableColumns(): SummaryTableFilterColumn[] { + return this._filterableColumns; + } + @Input() includeStatusFilter = false; @Input() includePrimaryNodeOnlyFilter = false; - @Output() filterChanged: EventEmitter = new EventEmitter(); + + @Input() set selectedNode(node: NodeSearchResult | null) { + const n: NodeSearchResult = node ? (node.id !== 'All' ? node : this.allNodes) : this.allNodes; + // find it in the available nodes + const found = this._clusterNodes.find((node) => node.id === n.id); + if (found) { + this.filterForm.get('clusterNode')?.setValue(found); + this._selectedNode = found; + } + } + + @Input() set clusterNodes(nodes: NodeSearchResult[] | null) { + if (!nodes) { + this._clusterNodes = []; + } else { + // test if the nodes have changed + if (!this.areSame(this._clusterNodes, nodes)) { + this._clusterNodes = [this.allNodes, ...nodes]; + if (this._selectedNode) { + this.selectedNode = this._selectedNode; + } + } + } + } + get clusterNodes(): NodeSearchResult[] { + return this._clusterNodes; + } + + private areSame(a: NodeSearchResult[], b: NodeSearchResult[]) { + if (a.length !== b.length) { + return false; + } + + const noMatch = a.filter((node) => b.findIndex((n) => n.id === node.id) < 0); + return noMatch.length === 0 || (noMatch.length === 1 && noMatch[0].id === 'All'); + } + + private areEqual(a: NodeSearchResult, b: NodeSearchResult) { + return a.id === b.id; + } + + @Output() filterChanged: EventEmitter = new EventEmitter(); + @Output() clusterFilterChanged: EventEmitter = + new EventEmitter(); @Input() set filterTerm(term: string) { this.filterForm.get('filterTerm')?.value(term); @@ -94,7 +158,8 @@ export class SummaryTableFilter implements AfterViewInit { filterTerm: '', filterColumn: this._initialFilterColumn || 'name', filterStatus: 'All', - primaryOnly: false + primaryOnly: false, + clusterNode: this.allNodes }); } @@ -106,7 +171,8 @@ export class SummaryTableFilter implements AfterViewInit { const filterColumn = this.filterForm.get('filterColumn')?.value; const filterStatus = this.filterForm.get('filterStatus')?.value; const primaryOnly = this.filterForm.get('primaryOnly')?.value; - this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly); + const clusterNode = this.filterForm.get('clusterNode')?.value; + this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly, clusterNode, 'filterTerm'); }); this.filterForm @@ -116,7 +182,8 @@ export class SummaryTableFilter implements AfterViewInit { const filterTerm = this.filterForm.get('filterTerm')?.value; const filterStatus = this.filterForm.get('filterStatus')?.value; const primaryOnly = this.filterForm.get('primaryOnly')?.value; - this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly); + const clusterNode = this.filterForm.get('clusterNode')?.value; + this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly, clusterNode, 'filterColumn'); }); this.filterForm @@ -126,7 +193,8 @@ export class SummaryTableFilter implements AfterViewInit { const filterTerm = this.filterForm.get('filterTerm')?.value; const filterColumn = this.filterForm.get('filterColumn')?.value; const primaryOnly = this.filterForm.get('primaryOnly')?.value; - this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly); + const clusterNode = this.filterForm.get('clusterNode')?.value; + this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly, clusterNode, 'filterStatus'); }); this.filterForm @@ -136,17 +204,45 @@ export class SummaryTableFilter implements AfterViewInit { const filterTerm = this.filterForm.get('filterTerm')?.value; const filterColumn = this.filterForm.get('filterColumn')?.value; const filterStatus = this.filterForm.get('filterStatus')?.value; - this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly); + const clusterNode = this.filterForm.get('clusterNode')?.value; + this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly, clusterNode, 'primaryOnly'); + }); + + this.filterForm + .get('clusterNode') + ?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((clusterNode) => { + if (this._selectedNode?.id !== clusterNode.id) { + this._selectedNode = clusterNode; + const filterTerm = this.filterForm.get('filterTerm')?.value; + const filterColumn = this.filterForm.get('filterColumn')?.value; + const filterStatus = this.filterForm.get('filterStatus')?.value; + const primaryOnly = this.filterForm.get('primaryOnly')?.value; + this.applyFilter(filterTerm, filterColumn, filterStatus, primaryOnly, clusterNode, 'clusterNode'); + } }); } - applyFilter(filterTerm: string, filterColumn: string, filterStatus: string, primaryOnly: boolean) { + applyFilter( + filterTerm: string, + filterColumn: string, + filterStatus: string, + primaryOnly: boolean, + clusterNode: NodeSearchResult, + changedField: string + ) { this.filterChanged.next({ filterColumn, filterStatus, filterTerm, - primaryOnly + primaryOnly, + clusterNode, + changedField }); - this.showFilterMatchedLabel = filterTerm?.length > 0 || filterStatus !== 'All' || primaryOnly; + this.showFilterMatchedLabel = + filterTerm?.length > 0 || + filterStatus !== 'All' || + primaryOnly || + (clusterNode ? clusterNode.id !== 'All' : false); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.html index 6fd0e42bd8..f72ce3afd3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.html @@ -22,14 +22,19 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.ts index e337cc5c50..bb8c89a265 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-listing.component.ts @@ -17,22 +17,31 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; -import { ConnectionStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; +import { SummaryListingState } from '../../state/summary-listing'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; import { selectConnectionIdFromRoute, selectConnectionStatus, selectConnectionStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus, selectViewStatusHistory } from '../../state/summary-listing/summary-listing.selectors'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; -import { filter, switchMap, take } from 'rxjs'; +import { filter, map, switchMap, take } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { getStatusHistoryAndOpenDialog } from '../../../../state/status-history/status-history.actions'; -import { ComponentType } from '../../../../state/shared'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { ConnectionStatusSnapshotEntity } from '../../state'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; @Component({ selector: 'connection-status-listing', @@ -45,6 +54,15 @@ export class ConnectionStatusListing { currentUser$ = this.store.select(selectCurrentUser); connectionStatusSnapshots$ = this.store.select(selectConnectionStatusSnapshots); selectedConnectionId$ = this.store.select(selectConnectionIdFromRoute); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); constructor(private store: Store) { this.store @@ -80,6 +98,7 @@ export class ConnectionStatusListing { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } selectConnection(connection: ConnectionStatusSnapshotEntity): void { @@ -103,4 +122,19 @@ export class ConnectionStatusListing { }) ); } + + viewClusteredDetails(processor: ConnectionStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: processor.id, + componentType: ComponentType.Connection + } + }) + ); + } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.html index 31cfc53bdd..7706656ae8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.html @@ -26,6 +26,8 @@ [includeStatusFilter]="false" [includePrimaryNodeOnlyFilter]="false" filterColumn="sourceName" + [clusterNodes]="clusterNodes" + [selectedNode]="selectedClusterNode" (filterChanged)="applyFilter($event)">
@@ -213,6 +215,13 @@ class="pointer fa fa-area-chart" title="View Status History" (click)="viewStatusHistoryClicked($event, item)">
+ + @if (connectedToCluster) { +
+ } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.scss index 5214b0d212..f6ac47ab86 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.scss @@ -23,8 +23,8 @@ } .mat-column-actions { - width: 72px; - min-width: 72px; + width: 80px; + min-width: 80px; } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.ts index 7202d0b9ea..df0e1f83ab 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/connection-status-listing/connection-status-table/connection-status-table.component.ts @@ -15,21 +15,18 @@ * limitations under the License. */ -import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SummaryTableFilterModule } from '../../common/summary-table-filter/summary-table-filter.module'; -import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; -import { MultiSort } from '../../common'; +import { MatSortModule, Sort } from '@angular/material/sort'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; -import { - SummaryTableFilterArgs, - SummaryTableFilterColumn -} from '../../common/summary-table-filter/summary-table-filter.component'; -import { ConnectionStatusSnapshot, ConnectionStatusSnapshotEntity } from '../../../state/summary-listing'; -import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { SummaryTableFilterColumn } from '../../common/summary-table-filter/summary-table-filter.component'; +import { MatTableModule } from '@angular/material/table'; import { ComponentType } from '../../../../../state/shared'; import { RouterLink } from '@angular/router'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { ConnectionStatusSnapshot, ConnectionStatusSnapshotEntity } from '../../../state'; +import { ComponentStatusTable } from '../../common/component-status-table/component-status-table.component'; export type SupportedColumns = 'name' | 'queue' | 'in' | 'out' | 'threshold' | 'sourceName' | 'destinationName'; @@ -40,26 +37,13 @@ export type SupportedColumns = 'name' | 'queue' | 'in' | 'out' | 'threshold' | ' templateUrl: './connection-status-table.component.html', styleUrls: ['./connection-status-table.component.scss'] }) -export class ConnectionStatusTable implements AfterViewInit { - private _initialSortColumn: SupportedColumns = 'sourceName'; - private _initialSortDirection: SortDirection = 'asc'; - +export class ConnectionStatusTable extends ComponentStatusTable { filterableColumns: SummaryTableFilterColumn[] = [ { key: 'sourceName', label: 'source' }, { key: 'name', label: 'name' }, { key: 'destinationName', label: 'destination' } ]; - totalCount = 0; - filteredCount = 0; - - multiSort: MultiSort = { - active: this._initialSortColumn, - direction: this._initialSortDirection, - sortValueIndex: 0, - totalValues: 2 - }; - displayedColumns: string[] = [ 'moreDetails', 'name', @@ -72,88 +56,19 @@ export class ConnectionStatusTable implements AfterViewInit { 'actions' ]; - dataSource: MatTableDataSource = - new MatTableDataSource(); - - @ViewChild(MatPaginator) paginator!: MatPaginator; - - constructor(private nifiCommon: NiFiCommon) {} - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; + constructor(private nifiCommon: NiFiCommon) { + super(); } - @Input() set initialSortColumn(initialSortColumn: SupportedColumns) { - this._initialSortColumn = initialSortColumn; - this.multiSort = { ...this.multiSort, active: initialSortColumn }; - } + override filterPredicate(data: ConnectionStatusSnapshotEntity, filter: string): boolean { + const { filterTerm, filterColumn } = JSON.parse(filter); - get initialSortColumn() { - return this._initialSortColumn; - } - - @Input() set initialSortDirection(initialSortDirection: SortDirection) { - this._initialSortDirection = initialSortDirection; - this.multiSort = { ...this.multiSort, direction: initialSortDirection }; - } - - get initialSortDirection() { - return this._initialSortDirection; - } - - @Input() selectedConnectionId!: string; - - @Input() set connections(connections: ConnectionStatusSnapshotEntity[]) { - if (connections) { - this.dataSource.data = this.sortEntities(connections, this.multiSort); - this.dataSource.filterPredicate = (data: ConnectionStatusSnapshotEntity, filter: string) => { - const { filterTerm, filterColumn } = JSON.parse(filter); - - if (filterTerm === '') { - return true; - } - - const field: string = data.connectionStatusSnapshot[ - filterColumn as keyof ConnectionStatusSnapshot - ] as string; - return this.nifiCommon.stringContains(field, filterTerm, true); - }; - - this.totalCount = connections.length; - this.filteredCount = connections.length; + if (filterTerm === '') { + return true; } - } - @Input() summaryListingStatus: string | null = null; - @Input() loadedTimestamp: string | null = null; - - @Output() refresh: EventEmitter = new EventEmitter(); - @Output() viewStatusHistory: EventEmitter = - new EventEmitter(); - @Output() selectConnection: EventEmitter = - new EventEmitter(); - @Output() clearSelection: EventEmitter = new EventEmitter(); - - resetPaginator(): void { - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); - } - } - - applyFilter(filter: SummaryTableFilterArgs) { - this.dataSource.filter = JSON.stringify(filter); - this.filteredCount = this.dataSource.filteredData.length; - this.resetPaginator(); - this.selectNone(); - } - - paginationChanged(): void { - // clear out any selection - this.selectNone(); - } - - private selectNone() { - this.clearSelection.next(); + const field: string = data.connectionStatusSnapshot[filterColumn as keyof ConnectionStatusSnapshot] as string; + return this.nifiCommon.stringContains(field, filterTerm, true); } getConnectionLink(connection: ConnectionStatusSnapshotEntity): string[] { @@ -165,54 +80,39 @@ export class ConnectionStatusTable implements AfterViewInit { ]; } - select(connection: ConnectionStatusSnapshotEntity): void { - this.selectConnection.next(connection); - } - - isSelected(connection: ConnectionStatusSnapshotEntity): boolean { - if (this.selectedConnectionId) { - return connection.id === this.selectedConnectionId; - } - return false; - } - canRead(connection: ConnectionStatusSnapshotEntity): boolean { return connection.canRead; } - sortData(sort: Sort) { - this.setMultiSort(sort); - this.dataSource.data = this.sortEntities(this.dataSource.data, sort); - } - formatName(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.name; } + formatSource(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.sourceName; } + formatDestination(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.destinationName; } + formatIn(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.input; } + formatOut(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.output; } + formatQueue(connection: ConnectionStatusSnapshotEntity): string { return connection.connectionStatusSnapshot.queued; } + formatThreshold(connection: ConnectionStatusSnapshotEntity): string { return `${connection.connectionStatusSnapshot.percentUseCount}% | ${connection.connectionStatusSnapshot.percentUseBytes}%`; } - viewStatusHistoryClicked(event: MouseEvent, connection: ConnectionStatusSnapshotEntity): void { - event.stopPropagation(); - this.viewStatusHistory.next(connection); - } - - private supportsMultiValuedSort(sort: Sort): boolean { + override supportsMultiValuedSort(sort: Sort): boolean { switch (sort.active) { case 'in': case 'out': @@ -224,29 +124,7 @@ export class ConnectionStatusTable implements AfterViewInit { } } - private setMultiSort(sort: Sort) { - const { active, direction, sortValueIndex, totalValues } = this.multiSort; - - if (this.supportsMultiValuedSort(sort)) { - if (active === sort.active) { - // previous sort was of the same column - if (direction === 'desc' && sort.direction === 'asc') { - // change from previous index to the next - const newIndex = sortValueIndex + 1 >= totalValues ? 0 : sortValueIndex + 1; - this.multiSort = { ...sort, sortValueIndex: newIndex, totalValues }; - } else { - this.multiSort = { ...sort, sortValueIndex, totalValues }; - } - } else { - // sorting a different column, just reset - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } else { - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } - - private sortEntities(data: ConnectionStatusSnapshotEntity[], sort: Sort): ConnectionStatusSnapshotEntity[] { + override sortEntities(data: ConnectionStatusSnapshotEntity[], sort: Sort): ConnectionStatusSnapshotEntity[] { if (!data) { return []; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.html index 901b3b6f63..ee0233a031 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.html @@ -22,14 +22,19 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.ts index 8c22deaf96..adae9ce68a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/input-port-status-listing/input-port-status-listing.component.ts @@ -19,14 +19,25 @@ import { Component } from '@angular/core'; import { selectInputPortIdFromRoute, selectInputPortStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus } from '../../state/summary-listing/summary-listing.selectors'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; -import { PortStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; +import { SummaryListingState } from '../../state/summary-listing'; import { Store } from '@ngrx/store'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { PortStatusSnapshotEntity } from '../../state'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; +import { map } from 'rxjs'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; @Component({ selector: 'input-port-status-listing', @@ -39,6 +50,15 @@ export class InputPortStatusListing { summaryListingStatus$ = this.store.select(selectSummaryListingStatus); currentUser$ = this.store.select(selectCurrentUser); selectedPortId$ = this.store.select(selectInputPortIdFromRoute); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); constructor(private store: Store) {} @@ -48,6 +68,7 @@ export class InputPortStatusListing { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } selectPort(port: PortStatusSnapshotEntity): void { @@ -63,4 +84,19 @@ export class InputPortStatusListing { clearSelection() { this.store.dispatch(SummaryListingActions.clearInputPortStatusSelection()); } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } + + viewClusteredDetails(port: PortStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: port.id, + componentType: ComponentType.InputPort + } + }) + ); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.html index 558c8178c0..df72786f27 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.html @@ -22,14 +22,19 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.ts index d069aa5e66..90482a4071 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/output-port-status-listing/output-port-status-listing.component.ts @@ -19,14 +19,25 @@ import { Component } from '@angular/core'; import { selectOutputPortIdFromRoute, selectOutputPortStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus } from '../../state/summary-listing/summary-listing.selectors'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; import { Store } from '@ngrx/store'; -import { PortStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; +import { SummaryListingState } from '../../state/summary-listing'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { PortStatusSnapshotEntity } from '../../state'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; +import { map } from 'rxjs'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; @Component({ selector: 'output-port-status-listing', @@ -39,6 +50,15 @@ export class OutputPortStatusListing { summaryListingStatus$ = this.store.select(selectSummaryListingStatus); currentUser$ = this.store.select(selectCurrentUser); selectedPortId$ = this.store.select(selectOutputPortIdFromRoute); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); constructor(private store: Store) {} @@ -48,6 +68,7 @@ export class OutputPortStatusListing { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } selectPort(port: PortStatusSnapshotEntity): void { @@ -63,4 +84,19 @@ export class OutputPortStatusListing { clearSelection() { this.store.dispatch(SummaryListingActions.clearOutputPortStatusSelection()); } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } + + viewClusteredDetails(port: PortStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: port.id, + componentType: ComponentType.OutputPort + } + }) + ); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.html index a38af8cf70..7d0af4cc0d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.html @@ -22,15 +22,20 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.ts index bbb7cc0830..468f63480b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.component.ts @@ -18,22 +18,31 @@ import { Component } from '@angular/core'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; -import { ProcessGroupStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; +import { SummaryListingState } from '../../state/summary-listing'; import { Store } from '@ngrx/store'; import { selectProcessGroupIdFromRoute, selectProcessGroupStatus, selectProcessGroupStatusItem, selectProcessGroupStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus, selectViewStatusHistory } from '../../state/summary-listing/summary-listing.selectors'; -import { filter, switchMap, take } from 'rxjs'; +import { filter, map, switchMap, take } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { getStatusHistoryAndOpenDialog } from '../../../../state/status-history/status-history.actions'; -import { ComponentType } from '../../../../state/shared'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { ProcessGroupStatusSnapshotEntity } from '../../state'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; @Component({ selector: 'process-group-status-listing', @@ -47,6 +56,15 @@ export class ProcessGroupStatusListing { currentUser$ = this.store.select(selectCurrentUser); selectedProcessGroupId$ = this.store.select(selectProcessGroupIdFromRoute); processGroupStatus$ = this.store.select(selectProcessGroupStatus); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); constructor(private store: Store) { this.store @@ -82,6 +100,7 @@ export class ProcessGroupStatusListing { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } viewStatusHistory(pg: ProcessGroupStatusSnapshotEntity): void { @@ -105,4 +124,19 @@ export class ProcessGroupStatusListing { clearSelection() { this.store.dispatch(SummaryListingActions.clearProcessGroupStatusSelection()); } + + viewClusteredDetails(pg: ProcessGroupStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: pg.id, + componentType: ComponentType.ProcessGroup + } + }) + ); + } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.module.ts index 59a02e71df..45e7289898 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-listing.module.ts @@ -20,10 +20,11 @@ import { ProcessGroupStatusListing } from './process-group-status-listing.compon import { CommonModule } from '@angular/common'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { ProcessGroupStatusTable } from './process-group-status-table/process-group-status-table.component'; +import { ProcessorStatusTable } from '../processor-status-listing/processor-status-table/processor-status-table.component'; @NgModule({ declarations: [ProcessGroupStatusListing], exports: [ProcessGroupStatusListing], - imports: [CommonModule, NgxSkeletonLoaderModule, ProcessGroupStatusTable] + imports: [CommonModule, NgxSkeletonLoaderModule, ProcessGroupStatusTable, ProcessorStatusTable] }) export class ProcessGroupStatusListingModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.html index 212bffb5ac..362e65487c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.html @@ -25,6 +25,8 @@ [filterableColumns]="filterableColumns" [includeStatusFilter]="false" [includePrimaryNodeOnlyFilter]="false" + [clusterNodes]="clusterNodes" + [selectedNode]="selectedClusterNode" (filterChanged)="applyFilter($event)">
@@ -341,6 +343,13 @@ class="pointer fa fa-area-chart" title="View Status History" (click)="viewStatusHistoryClicked($event, item)">
+ + @if (connectedToCluster) { +
+ } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.scss index 802193673e..ece5fddef0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.scss @@ -23,8 +23,8 @@ } .mat-column-actions { - width: 72px; - min-width: 72px; + width: 80px; + min-width: 80px; } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.ts index dbe93501a8..99fc53ed28 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/process-group-status-listing/process-group-status-table/process-group-status-table.component.ts @@ -15,20 +15,17 @@ * limitations under the License. */ -import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; -import { MultiSort } from '../../common'; -import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { MatTableModule } from '@angular/material/table'; import { SummaryTableFilterModule } from '../../common/summary-table-filter/summary-table-filter.module'; -import { ProcessGroupStatusSnapshot, ProcessGroupStatusSnapshotEntity } from '../../../state/summary-listing'; -import { - SummaryTableFilterArgs, - SummaryTableFilterColumn -} from '../../common/summary-table-filter/summary-table-filter.component'; +import { SummaryTableFilterColumn } from '../../common/summary-table-filter/summary-table-filter.component'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; import { RouterLink } from '@angular/router'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { ProcessGroupStatusSnapshot, ProcessGroupStatusSnapshotEntity } from '../../../state'; +import { ComponentStatusTable } from '../../common/component-status-table/component-status-table.component'; export type SupportedColumns = | 'name' @@ -49,20 +46,8 @@ export type SupportedColumns = templateUrl: './process-group-status-table.component.html', styleUrls: ['./process-group-status-table.component.scss'] }) -export class ProcessGroupStatusTable implements AfterViewInit { - private _initialSortColumn: SupportedColumns = 'name'; - private _initialSortDirection: SortDirection = 'asc'; - +export class ProcessGroupStatusTable extends ComponentStatusTable { filterableColumns: SummaryTableFilterColumn[] = [{ key: 'name', label: 'name' }]; - totalCount = 0; - filteredCount = 0; - - multiSort: MultiSort = { - active: this._initialSortColumn, - direction: this._initialSortDirection, - sortValueIndex: 0, - totalValues: 2 - }; displayedColumns: string[] = [ 'moreDetails', @@ -79,87 +64,23 @@ export class ProcessGroupStatusTable implements AfterViewInit { 'actions' ]; - dataSource: MatTableDataSource = - new MatTableDataSource(); - - constructor(private nifiCommon: NiFiCommon) {} - - applyFilter(filter: SummaryTableFilterArgs) { - this.dataSource.filter = JSON.stringify(filter); - this.filteredCount = this.dataSource.filteredData.length; - this.resetPaginator(); - this.selectNone(); - } - - @Input() selectedProcessGroupId!: string; - - @Input() set initialSortColumn(initialSortColumn: SupportedColumns) { - this._initialSortColumn = initialSortColumn; - this.multiSort = { ...this.multiSort, active: initialSortColumn }; - } - - get initialSortColumn() { - return this._initialSortColumn; - } - - @Input() set initialSortDirection(initialSortDirection: SortDirection) { - this._initialSortDirection = initialSortDirection; - this.multiSort = { ...this.multiSort, direction: initialSortDirection }; - } - - get initialSortDirection() { - return this._initialSortDirection; + constructor(private nifiCommon: NiFiCommon) { + super(); } @Input() rootProcessGroup!: ProcessGroupStatusSnapshot; - @Input() set processGroups(processGroups: ProcessGroupStatusSnapshotEntity[]) { - if (processGroups) { - this.dataSource.data = this.sortEntities(processGroups, this.multiSort); + override filterPredicate(data: ProcessGroupStatusSnapshotEntity, filter: string): boolean { + const { filterTerm, filterColumn } = JSON.parse(filter); - this.dataSource.filterPredicate = (data: ProcessGroupStatusSnapshotEntity, filter: string): boolean => { - const { filterTerm, filterColumn } = JSON.parse(filter); - - if (filterTerm === '') { - return true; - } - - const field: string = data.processGroupStatusSnapshot[ - filterColumn as keyof ProcessGroupStatusSnapshot - ] as string; - return this.nifiCommon.stringContains(field, filterTerm, true); - }; - - this.totalCount = processGroups.length; - this.filteredCount = processGroups.length; + if (filterTerm === '') { + return true; } - } - @Input() summaryListingStatus: string | null = null; - @Input() loadedTimestamp: string | null = null; - - @Output() viewStatusHistory: EventEmitter = - new EventEmitter(); - @Output() selectProcessGroup: EventEmitter = - new EventEmitter(); - @Output() clearSelection: EventEmitter = new EventEmitter(); - @Output() refresh: EventEmitter = new EventEmitter(); - - @ViewChild(MatPaginator) paginator!: MatPaginator; - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; - } - - resetPaginator(): void { - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); - } - } - - paginationChanged(): void { - // clear out any selection - this.selectNone(); + const field: string = data.processGroupStatusSnapshot[ + filterColumn as keyof ProcessGroupStatusSnapshot + ] as string; + return this.nifiCommon.stringContains(field, filterTerm, true); } formatName(pg: ProcessGroupStatusSnapshotEntity): string { @@ -252,7 +173,7 @@ export class ProcessGroupStatusTable implements AfterViewInit { return 0; } - private supportsMultiValuedSort(sort: Sort): boolean { + override supportsMultiValuedSort(sort: Sort): boolean { switch (sort.active) { case 'transferred': case 'in': @@ -268,34 +189,7 @@ export class ProcessGroupStatusTable implements AfterViewInit { } } - private setMultiSort(sort: Sort) { - const { active, direction, sortValueIndex, totalValues } = this.multiSort; - - if (this.supportsMultiValuedSort(sort)) { - if (active === sort.active) { - // previous sort was of the same column - if (direction === 'desc' && sort.direction === 'asc') { - // change from previous index to the next - const newIndex = sortValueIndex + 1 >= totalValues ? 0 : sortValueIndex + 1; - this.multiSort = { ...sort, sortValueIndex: newIndex, totalValues }; - } else { - this.multiSort = { ...sort, sortValueIndex, totalValues }; - } - } else { - // sorting a different column, just reset - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } else { - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } - - sortData(sort: Sort) { - this.setMultiSort(sort); - this.dataSource.data = this.sortEntities(this.dataSource.data, sort); - } - - private sortEntities(data: ProcessGroupStatusSnapshotEntity[], sort: Sort): ProcessGroupStatusSnapshotEntity[] { + override sortEntities(data: ProcessGroupStatusSnapshotEntity[], sort: Sort): ProcessGroupStatusSnapshotEntity[] { if (!data) { return []; } @@ -438,24 +332,4 @@ export class ProcessGroupStatusTable implements AfterViewInit { getProcessGroupLink(pg: ProcessGroupStatusSnapshotEntity): string[] { return ['/process-groups', pg.id]; } - - select(pg: ProcessGroupStatusSnapshotEntity): void { - this.selectProcessGroup.next(pg); - } - - isSelected(pg: ProcessGroupStatusSnapshotEntity): boolean { - if (this.selectedProcessGroupId) { - return pg.id === this.selectedProcessGroupId; - } - return false; - } - - viewStatusHistoryClicked(event: MouseEvent, pg: ProcessGroupStatusSnapshotEntity): void { - event.stopPropagation(); - this.viewStatusHistory.next(pg); - } - - private selectNone() { - this.clearSelection.next(); - } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.html index 2916eac3d0..b09f131ac1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.html @@ -21,14 +21,19 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.ts index cc56c69e24..546f6fc2fd 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-listing.component.ts @@ -21,20 +21,29 @@ import { selectProcessorIdFromRoute, selectProcessorStatus, selectProcessorStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus, selectViewStatusHistory } from '../../state/summary-listing/summary-listing.selectors'; -import { ProcessorStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; +import { SummaryListingState } from '../../state/summary-listing'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import { getStatusHistoryAndOpenDialog } from '../../../../state/status-history/status-history.actions'; -import { ComponentType } from '../../../../state/shared'; -import { combineLatest, delay, filter, Subject, switchMap, take } from 'rxjs'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; +import { combineLatest, delay, filter, map, Subject, switchMap, take } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; import { MatPaginator } from '@angular/material/paginator'; import { ProcessorStatusTable } from './processor-status-table/processor-status-table.component'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { ProcessorStatusSnapshotEntity } from '../../state'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; @Component({ selector: 'processor-status-listing', @@ -46,6 +55,15 @@ export class ProcessorStatusListing implements AfterViewInit { loadedTimestamp$ = this.store.select(selectSummaryListingLoadedTimestamp); summaryListingStatus$ = this.store.select(selectSummaryListingStatus); selectedProcessorId$ = this.store.select(selectProcessorIdFromRoute); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); currentUser$ = this.store.select(selectCurrentUser); @@ -104,6 +122,7 @@ export class ProcessorStatusListing implements AfterViewInit { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } viewStatusHistory(processor: ProcessorStatusSnapshotEntity): void { @@ -127,4 +146,19 @@ export class ProcessorStatusListing implements AfterViewInit { clearSelection() { this.store.dispatch(SummaryListingActions.clearProcessorStatusSelection()); } + + viewClusteredDetails(processor: ProcessorStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: processor.id, + componentType: ComponentType.Processor + } + }) + ); + } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/_processor-status-table.component-theme.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/_processor-status-table.component-theme.scss new file mode 100644 index 0000000000..0ec9510c25 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/_processor-status-table.component-theme.scss @@ -0,0 +1,42 @@ +/*! + * 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 'sass:map'; +@use '@angular/material' as mat; + +@mixin nifi-theme($material-theme) { + // Get the color config from the theme. + $color-config: mat.get-color-config($material-theme); + + // Get the color palette from the color-config. + $accent-palette: map.get($color-config, 'accent'); + $primary-palette: map.get($color-config, 'primary'); + + // Get hues from palette + $accent-palette-A400: mat.get-color-from-palette($accent-palette, 'A400'); + $primary-palette-contrast-300: mat.get-color-from-palette($primary-palette, '300-contrast'); + $primary-palette-300: mat.get-color-from-palette($primary-palette, 300); + $primary-palette-900: mat.get-color-from-palette($primary-palette, 900); + + .processor-status-table { + .primary-node-only { + color: $accent-palette-A400; + background-color: $primary-palette-contrast-300; + border: 1px solid $primary-palette-300; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.html index 54da6350b7..833c487863 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.html @@ -25,6 +25,8 @@ [filterableColumns]="filterableColumns" [includeStatusFilter]="true" [includePrimaryNodeOnlyFilter]="true" + [clusterNodes]="clusterNodes" + [selectedNode]="selectedClusterNode" (filterChanged)="applyFilter($event)">
@@ -60,7 +62,16 @@
Name
- {{ formatName(item) }} +
+ @if (item.processorStatusSnapshot.executionNode === 'PRIMARY') { +
+ P +
+ } +
{{ formatName(item) }}
+
@@ -248,6 +259,13 @@ class="pointer fa fa-area-chart" title="View Status History" (click)="viewStatusHistoryClicked($event, item)">
+ + @if (connectedToCluster) { +
+ } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.scss index fa1fa87a66..e4f2e7fc8d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.scss @@ -22,8 +22,19 @@ } .mat-column-actions { - width: 72px; - min-width: 72px; + width: 80px; + min-width: 80px; } } + .primary-node-only { + font-family: Roboto; + font-size: 10px; + font-weight: bold; + line-height: 14px; + width: 16px; + height: 16px; + border-radius: 8px; + float: left; + text-align: center; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.ts index afab586787..f83e659039 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component.ts @@ -15,21 +15,18 @@ * limitations under the License. */ -import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { ProcessorStatusSnapshot, ProcessorStatusSnapshotEntity } from '../../../state/summary-listing'; -import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; -import { - SummaryTableFilterArgs, - SummaryTableFilterColumn -} from '../../common/summary-table-filter/summary-table-filter.component'; +import { Component } from '@angular/core'; +import { MatTableModule } from '@angular/material/table'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { SummaryTableFilterColumn } from '../../common/summary-table-filter/summary-table-filter.component'; import { RouterLink } from '@angular/router'; import { SummaryTableFilterModule } from '../../common/summary-table-filter/summary-table-filter.module'; import { NgClass } from '@angular/common'; import { ComponentType } from '../../../../../state/shared'; -import { MultiSort } from '../../common'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { ProcessorStatusSnapshot, ProcessorStatusSnapshotEntity } from '../../../state'; +import { ComponentStatusTable } from '../../common/component-status-table/component-status-table.component'; export type SupportedColumns = 'name' | 'type' | 'processGroup' | 'runStatus' | 'in' | 'out' | 'readWrite' | 'tasks'; @@ -40,23 +37,11 @@ export type SupportedColumns = 'name' | 'type' | 'processGroup' | 'runStatus' | standalone: true, imports: [RouterLink, SummaryTableFilterModule, MatTableModule, MatSortModule, NgClass, MatPaginatorModule] }) -export class ProcessorStatusTable implements AfterViewInit { - private _initialSortColumn: SupportedColumns = 'name'; - private _initialSortDirection: SortDirection = 'asc'; - +export class ProcessorStatusTable extends ComponentStatusTable { filterableColumns: SummaryTableFilterColumn[] = [ { key: 'name', label: 'name' }, { key: 'type', label: 'type' } ]; - totalCount = 0; - filteredCount = 0; - - multiSort: MultiSort = { - active: this._initialSortColumn, - direction: this._initialSortDirection, - sortValueIndex: 0, - totalValues: 2 - }; displayedColumns: string[] = [ 'moreDetails', @@ -70,95 +55,9 @@ export class ProcessorStatusTable implements AfterViewInit { 'tasks', 'actions' ]; - dataSource: MatTableDataSource = - new MatTableDataSource(); - @ViewChild(MatPaginator) paginator!: MatPaginator; - - constructor(private nifiCommon: NiFiCommon) {} - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; - } - - applyFilter(filter: SummaryTableFilterArgs) { - this.dataSource.filter = JSON.stringify(filter); - this.filteredCount = this.dataSource.filteredData.length; - this.resetPaginator(); - this.selectNone(); - } - - @Input() selectedProcessorId!: string; - - @Input() set initialSortColumn(initialSortColumn: SupportedColumns) { - this._initialSortColumn = initialSortColumn; - this.multiSort = { ...this.multiSort, active: initialSortColumn }; - } - - get initialSortColumn() { - return this._initialSortColumn; - } - - @Input() set initialSortDirection(initialSortDirection: SortDirection) { - this._initialSortDirection = initialSortDirection; - this.multiSort = { ...this.multiSort, direction: initialSortDirection }; - } - - get initialSortDirection() { - return this._initialSortDirection; - } - - @Input() set processors(processors: ProcessorStatusSnapshotEntity[]) { - if (processors) { - this.dataSource.data = this.sortEntities(processors, this.multiSort); - this.dataSource.filterPredicate = (data: ProcessorStatusSnapshotEntity, filter: string): boolean => { - const { filterTerm, filterColumn, filterStatus, primaryOnly } = JSON.parse(filter); - const matchOnStatus: boolean = filterStatus !== 'All'; - - if (primaryOnly) { - if (data.processorStatusSnapshot.executionNode !== 'PRIMARY') { - return false; - } - } - if (matchOnStatus) { - if (data.processorStatusSnapshot.runStatus !== filterStatus) { - return false; - } - } - if (filterTerm === '') { - return true; - } - - const field: string = data.processorStatusSnapshot[ - filterColumn as keyof ProcessorStatusSnapshot - ] as string; - return this.nifiCommon.stringContains(field, filterTerm, true); - }; - - this.totalCount = processors.length; - this.filteredCount = processors.length; - } - } - - @Input() summaryListingStatus: string | null = null; - @Input() loadedTimestamp: string | null = null; - - @Output() refresh: EventEmitter = new EventEmitter(); - @Output() viewStatusHistory: EventEmitter = - new EventEmitter(); - @Output() selectProcessor: EventEmitter = - new EventEmitter(); - @Output() clearSelection: EventEmitter = new EventEmitter(); - - resetPaginator(): void { - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); - } - } - - paginationChanged(): void { - // clear out any selection - this.selectNone(); + constructor(private nifiCommon: NiFiCommon) { + super(); } formatName(processor: ProcessorStatusSnapshotEntity): string { @@ -220,12 +119,7 @@ export class ProcessorStatusTable implements AfterViewInit { } } - sortData(sort: Sort) { - this.setMultiSort(sort); - this.dataSource.data = this.sortEntities(this.dataSource.data, sort); - } - - private sortEntities(data: ProcessorStatusSnapshotEntity[], sort: Sort): ProcessorStatusSnapshotEntity[] { + override sortEntities(data: ProcessorStatusSnapshotEntity[], sort: Sort): ProcessorStatusSnapshotEntity[] { if (!data) { return []; } @@ -302,11 +196,7 @@ export class ProcessorStatusTable implements AfterViewInit { }); } - private compare(a: number | string, b: number | string, isAsc: boolean) { - return (a < b ? -1 : a > b ? 1 : 0) * (isAsc ? 1 : -1); - } - - private supportsMultiValuedSort(sort: Sort): boolean { + override supportsMultiValuedSort(sort: Sort): boolean { switch (sort.active) { case 'in': case 'out': @@ -318,45 +208,25 @@ export class ProcessorStatusTable implements AfterViewInit { } } - private setMultiSort(sort: Sort) { - const { active, direction, sortValueIndex, totalValues } = this.multiSort; + override filterPredicate(data: ProcessorStatusSnapshotEntity, filter: string): boolean { + const { filterTerm, filterColumn, filterStatus, primaryOnly } = JSON.parse(filter); + const matchOnStatus: boolean = filterStatus !== 'All'; - if (this.supportsMultiValuedSort(sort)) { - if (active === sort.active) { - // previous sort was of the same column - if (direction === 'desc' && sort.direction === 'asc') { - // change from previous index to the next - const newIndex = sortValueIndex + 1 >= totalValues ? 0 : sortValueIndex + 1; - this.multiSort = { ...sort, sortValueIndex: newIndex, totalValues }; - } else { - this.multiSort = { ...sort, sortValueIndex, totalValues }; - } - } else { - // sorting a different column, just reset - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; + if (primaryOnly) { + if (data.processorStatusSnapshot.executionNode !== 'PRIMARY') { + return false; } - } else { - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; } - } - - select(processor: ProcessorStatusSnapshotEntity): void { - this.selectProcessor.next(processor); - } - - isSelected(processor: ProcessorStatusSnapshotEntity): boolean { - if (this.selectedProcessorId) { - return processor.id === this.selectedProcessorId; + if (matchOnStatus) { + if (data.processorStatusSnapshot.runStatus !== filterStatus) { + return false; + } + } + if (filterTerm === '') { + return true; } - return false; - } - viewStatusHistoryClicked(event: MouseEvent, processor: ProcessorStatusSnapshotEntity): void { - event.stopPropagation(); - this.viewStatusHistory.next(processor); - } - - private selectNone() { - this.clearSelection.next(); + const field: string = data.processorStatusSnapshot[filterColumn as keyof ProcessorStatusSnapshot] as string; + return this.nifiCommon.stringContains(field, filterTerm, true); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.html index d5e655c08d..40e77d95d1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.html @@ -22,14 +22,19 @@ } @else { } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.ts index b1981e02ea..76872ff185 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-listing.component.ts @@ -20,19 +20,28 @@ import { selectRemoteProcessGroupIdFromRoute, selectRemoteProcessGroupStatus, selectRemoteProcessGroupStatusSnapshots, + selectSelectedClusterNode, selectSummaryListingLoadedTimestamp, selectSummaryListingStatus, selectViewStatusHistory } from '../../state/summary-listing/summary-listing.selectors'; import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; import { Store } from '@ngrx/store'; -import { RemoteProcessGroupStatusSnapshotEntity, SummaryListingState } from '../../state/summary-listing'; -import { filter, switchMap, take } from 'rxjs'; +import { SummaryListingState } from '../../state/summary-listing'; +import { filter, map, switchMap, take } from 'rxjs'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { getStatusHistoryAndOpenDialog } from '../../../../state/status-history/status-history.actions'; -import { ComponentType } from '../../../../state/shared'; +import { ComponentType, isDefinedAndNotNull } from '../../../../state/shared'; import { initialState } from '../../state/summary-listing/summary-listing.reducer'; import * as SummaryListingActions from '../../state/summary-listing/summary-listing.actions'; +import { loadClusterSummary } from '../../../../state/cluster-summary/cluster-summary.actions'; +import { RemoteProcessGroupStatusSnapshotEntity } from '../../state'; +import { + selectClusterSearchResults, + selectClusterSummary +} from '../../../../state/cluster-summary/cluster-summary.selectors'; +import * as ClusterStatusActions from '../../state/component-cluster-status/component-cluster-status.actions'; +import { NodeSearchResult } from '../../../../state/cluster-summary'; @Component({ selector: 'remote-process-group-status-listing', @@ -45,6 +54,15 @@ export class RemoteProcessGroupStatusListing { currentUser$ = this.store.select(selectCurrentUser); rpgStatusSnapshots$ = this.store.select(selectRemoteProcessGroupStatusSnapshots); selectedRpgId$ = this.store.select(selectRemoteProcessGroupIdFromRoute); + connectedToCluster$ = this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + map((cluster) => cluster.connectedToCluster) + ); + clusterNodes$ = this.store.select(selectClusterSearchResults).pipe( + isDefinedAndNotNull(), + map((results) => results.nodeResults) + ); + selectedClusterNode$ = this.store.select(selectSelectedClusterNode); constructor(private store: Store) { this.store @@ -80,6 +98,7 @@ export class RemoteProcessGroupStatusListing { refreshSummaryListing() { this.store.dispatch(SummaryListingActions.loadSummaryListing({ recursive: true })); + this.store.dispatch(loadClusterSummary()); } selectRemoteProcessGroup(rpg: RemoteProcessGroupStatusSnapshotEntity): void { @@ -103,4 +122,19 @@ export class RemoteProcessGroupStatusListing { }) ); } + + viewClusteredDetails(processor: RemoteProcessGroupStatusSnapshotEntity): void { + this.store.dispatch( + ClusterStatusActions.loadComponentClusterStatusAndOpenDialog({ + request: { + id: processor.id, + componentType: ComponentType.RemoteProcessGroup + } + }) + ); + } + + clusterNodeSelected(clusterNode: NodeSearchResult) { + this.store.dispatch(SummaryListingActions.selectClusterNode({ clusterNode })); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.html index 72cbfe8070..a2754c9d7c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.html @@ -26,6 +26,8 @@ [filterableColumns]="filterableColumns" [includeStatusFilter]="false" [includePrimaryNodeOnlyFilter]="false" + [clusterNodes]="clusterNodes" + [selectedNode]="selectedClusterNode" (filterChanged)="applyFilter($event)">
@@ -155,6 +157,13 @@ class="pointer fa fa-area-chart" title="View Status History" (click)="viewStatusHistoryClicked($event, item)">
+ + @if (connectedToCluster) { +
+ } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.scss index 6be7cb541d..80086e7ca0 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.scss @@ -23,8 +23,8 @@ } .mat-column-actions { - width: 72px; - min-width: 72px; + width: 80px; + min-width: 80px; } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.ts index f75a8ea727..554c564bcb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/summary/ui/remote-process-group-status-listing/remote-process-group-status-table/remote-process-group-status-table.component.ts @@ -15,24 +15,18 @@ * limitations under the License. */ -import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SummaryTableFilterModule } from '../../common/summary-table-filter/summary-table-filter.module'; -import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; -import { - SummaryTableFilterArgs, - SummaryTableFilterColumn -} from '../../common/summary-table-filter/summary-table-filter.component'; -import { MultiSort } from '../../common'; -import { MatTableDataSource, MatTableModule } from '@angular/material/table'; -import { - RemoteProcessGroupStatusSnapshot, - RemoteProcessGroupStatusSnapshotEntity -} from '../../../state/summary-listing'; +import { MatSortModule, Sort } from '@angular/material/sort'; +import { SummaryTableFilterColumn } from '../../common/summary-table-filter/summary-table-filter.component'; +import { MatTableModule } from '@angular/material/table'; import { NiFiCommon } from '../../../../../service/nifi-common.service'; import { ComponentType } from '../../../../../state/shared'; import { RouterLink } from '@angular/router'; -import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { RemoteProcessGroupStatusSnapshot, RemoteProcessGroupStatusSnapshotEntity } from '../../../state'; +import { ComponentStatusTable } from '../../common/component-status-table/component-status-table.component'; export type SupportedColumns = 'name' | 'uri' | 'transmitting' | 'sent' | 'received'; @@ -43,105 +37,28 @@ export type SupportedColumns = 'name' | 'uri' | 'transmitting' | 'sent' | 'recei templateUrl: './remote-process-group-status-table.component.html', styleUrls: ['./remote-process-group-status-table.component.scss'] }) -export class RemoteProcessGroupStatusTable implements AfterViewInit { - private _initialSortColumn: SupportedColumns = 'name'; - private _initialSortDirection: SortDirection = 'asc'; - +export class RemoteProcessGroupStatusTable extends ComponentStatusTable { filterableColumns: SummaryTableFilterColumn[] = [ { key: 'name', label: 'name' }, { key: 'targetUri', label: 'uri' } ]; - - totalCount = 0; - filteredCount = 0; - - multiSort: MultiSort = { - active: this._initialSortColumn, - direction: this._initialSortDirection, - sortValueIndex: 0, - totalValues: 2 - }; - displayedColumns: string[] = ['moreDetails', 'name', 'uri', 'transmitting', 'sent', 'received', 'actions']; - dataSource: MatTableDataSource = - new MatTableDataSource(); - - @ViewChild(MatPaginator) paginator!: MatPaginator; - - ngAfterViewInit(): void { - this.dataSource.paginator = this.paginator; + constructor(private nifiCommon: NiFiCommon) { + super(); } - constructor(private nifiCommon: NiFiCommon) {} + override filterPredicate(data: RemoteProcessGroupStatusSnapshotEntity, filter: string): boolean { + const { filterTerm, filterColumn } = JSON.parse(filter); - @Input() set initialSortColumn(initialSortColumn: SupportedColumns) { - this._initialSortColumn = initialSortColumn; - this.multiSort = { ...this.multiSort, active: initialSortColumn }; - } - - get initialSortColumn() { - return this._initialSortColumn; - } - - @Input() set initialSortDirection(initialSortDirection: SortDirection) { - this._initialSortDirection = initialSortDirection; - this.multiSort = { ...this.multiSort, direction: initialSortDirection }; - } - - get initialSortDirection() { - return this._initialSortDirection; - } - - @Input() selectedRemoteProcessGroupId!: string; - - @Input() set remoteProcessGroups(rpgs: RemoteProcessGroupStatusSnapshotEntity[]) { - if (rpgs) { - this.dataSource.data = this.sortEntities(rpgs, this.multiSort); - this.dataSource.filterPredicate = (data: RemoteProcessGroupStatusSnapshotEntity, filter: string) => { - const { filterTerm, filterColumn } = JSON.parse(filter); - - if (filterTerm === '') { - return true; - } - - const field: string = data.remoteProcessGroupStatusSnapshot[ - filterColumn as keyof RemoteProcessGroupStatusSnapshot - ] as string; - return this.nifiCommon.stringContains(field, filterTerm, true); - }; - - this.totalCount = rpgs.length; - this.filteredCount = rpgs.length; + if (filterTerm === '') { + return true; } - } - @Input() summaryListingStatus: string | null = null; - @Input() loadedTimestamp: string | null = null; - - @Output() refresh: EventEmitter = new EventEmitter(); - @Output() viewStatusHistory: EventEmitter = - new EventEmitter(); - @Output() selectRemoteProcessGroup: EventEmitter = - new EventEmitter(); - @Output() clearSelection: EventEmitter = new EventEmitter(); - - applyFilter(filter: SummaryTableFilterArgs) { - this.dataSource.filter = JSON.stringify(filter); - this.filteredCount = this.dataSource.filteredData.length; - this.resetPaginator(); - this.selectNone(); - } - - resetPaginator(): void { - if (this.dataSource.paginator) { - this.dataSource.paginator.firstPage(); - } - } - - paginationChanged(): void { - // clear out any selection - this.selectNone(); + const field: string = data.remoteProcessGroupStatusSnapshot[ + filterColumn as keyof RemoteProcessGroupStatusSnapshot + ] as string; + return this.nifiCommon.stringContains(field, filterTerm, true); } getRemoteProcessGroupLink(rpg: RemoteProcessGroupStatusSnapshotEntity): string[] { @@ -153,35 +70,10 @@ export class RemoteProcessGroupStatusTable implements AfterViewInit { ]; } - select(rpg: RemoteProcessGroupStatusSnapshotEntity) { - this.selectRemoteProcessGroup.next(rpg); - } - - private selectNone() { - this.clearSelection.next(); - } - - isSelected(rpg: RemoteProcessGroupStatusSnapshotEntity): boolean { - if (this.selectedRemoteProcessGroupId) { - return rpg.id === this.selectedRemoteProcessGroupId; - } - return false; - } - canRead(rpg: RemoteProcessGroupStatusSnapshotEntity): boolean { return rpg.canRead; } - sortData(sort: Sort) { - this.setMultiSort(sort); - this.dataSource.data = this.sortEntities(this.dataSource.data, sort); - } - - viewStatusHistoryClicked(event: MouseEvent, rpg: RemoteProcessGroupStatusSnapshotEntity): void { - event.stopPropagation(); - this.viewStatusHistory.next(rpg); - } - formatName(rpg: RemoteProcessGroupStatusSnapshotEntity): string { return rpg.remoteProcessGroupStatusSnapshot.name; } @@ -214,7 +106,7 @@ export class RemoteProcessGroupStatusTable implements AfterViewInit { } } - private supportsMultiValuedSort(sort: Sort): boolean { + override supportsMultiValuedSort(sort: Sort): boolean { switch (sort.active) { case 'sent': case 'received': @@ -224,29 +116,7 @@ export class RemoteProcessGroupStatusTable implements AfterViewInit { } } - private setMultiSort(sort: Sort) { - const { active, direction, sortValueIndex, totalValues } = this.multiSort; - - if (this.supportsMultiValuedSort(sort)) { - if (active === sort.active) { - // previous sort was of the same column - if (direction === 'desc' && sort.direction === 'asc') { - // change from previous index to the next - const newIndex = sortValueIndex + 1 >= totalValues ? 0 : sortValueIndex + 1; - this.multiSort = { ...sort, sortValueIndex: newIndex, totalValues }; - } else { - this.multiSort = { ...sort, sortValueIndex, totalValues }; - } - } else { - // sorting a different column, just reset - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } else { - this.multiSort = { ...sort, sortValueIndex: 0, totalValues }; - } - } - - private sortEntities( + override sortEntities( data: RemoteProcessGroupStatusSnapshotEntity[], sort: Sort ): RemoteProcessGroupStatusSnapshotEntity[] { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.spec.ts new file mode 100644 index 0000000000..fbcac00d14 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.spec.ts @@ -0,0 +1,25 @@ +/* + * 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 { ComponentTypeNamePipe } from './component-type-name.pipe'; + +describe('ComponentTypeNamePipe', () => { + it('create an instance', () => { + const pipe = new ComponentTypeNamePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.ts new file mode 100644 index 0000000000..e76c950ae3 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pipes/component-type-name.pipe.ts @@ -0,0 +1,60 @@ +/* + * 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 { Pipe, PipeTransform } from '@angular/core'; +import { ComponentType } from '../state/shared'; + +@Pipe({ + name: 'componentTypeName', + standalone: true +}) +export class ComponentTypeNamePipe implements PipeTransform { + transform(type: ComponentType): string { + switch (type) { + case ComponentType.Connection: + return 'Connection'; + case ComponentType.Processor: + return 'Processor'; + case ComponentType.OutputPort: + return 'Output Port'; + case ComponentType.InputPort: + return 'Input Port'; + case ComponentType.ProcessGroup: + return 'Process Group'; + case ComponentType.ControllerService: + return 'Controller Service'; + case ComponentType.Flow: + return 'Flow'; + case ComponentType.FlowAnalysisRule: + return 'Flow Analysis Rule'; + case ComponentType.FlowRegistryClient: + return 'Flow Registry Client'; + case ComponentType.Funnel: + return 'Funnel'; + case ComponentType.Label: + return 'Label'; + case ComponentType.ParameterProvider: + return 'Parameter Provider'; + case ComponentType.RemoteProcessGroup: + return 'Remote Process Group'; + case ComponentType.ReportingTask: + return 'Reporting Task'; + default: + return ''; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.actions.ts index 5adea058db..92256d16b7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.actions.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.actions.ts @@ -16,7 +16,7 @@ */ import { createAction, props } from '@ngrx/store'; -import { LoadClusterSummaryResponse } from './index'; +import { ClusterSearchRequest, ClusterSearchResults, LoadClusterSummaryResponse } from './index'; const CLUSTER_SUMMARY_STATE_PREFIX = '[Cluster Summary State]'; @@ -37,3 +37,13 @@ export const clusterSummaryApiError = createAction( ); export const clearClusterSummaryApiError = createAction(`${CLUSTER_SUMMARY_STATE_PREFIX} Clear About Api Error`); + +export const searchCluster = createAction( + `${CLUSTER_SUMMARY_STATE_PREFIX} Search Cluster`, + props<{ request: ClusterSearchRequest }>() +); + +export const searchClusterSuccess = createAction( + `${CLUSTER_SUMMARY_STATE_PREFIX} Search Cluster Success`, + props<{ response: ClusterSearchResults }>() +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.effects.ts index cd6c4e8fbd..4256ca868b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.effects.ts @@ -16,16 +16,23 @@ */ import { Injectable } from '@angular/core'; -import { Actions, createEffect, ofType } from '@ngrx/effects'; +import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import * as ClusterSummaryActions from './cluster-summary.actions'; -import { asyncScheduler, catchError, from, interval, map, of, switchMap, takeUntil } from 'rxjs'; +import { asyncScheduler, catchError, filter, from, interval, map, of, switchMap, takeUntil } from 'rxjs'; import { ClusterService } from '../../service/cluster.service'; +import { selectClusterSummary } from './cluster-summary.selectors'; +import { isDefinedAndNotNull } from '../shared'; +import { Store } from '@ngrx/store'; +import { ClusterSummaryState } from './index'; +import { HttpErrorResponse } from '@angular/common/http'; +import * as ErrorActions from '../error/error.actions'; @Injectable() export class ClusterSummaryEffects { constructor( private actions$: Actions, - private clusterService: ClusterService + private clusterService: ClusterService, + private store: Store ) {} loadClusterSummary$ = createEffect(() => @@ -57,4 +64,29 @@ export class ClusterSummaryEffects { switchMap(() => of(ClusterSummaryActions.loadClusterSummary())) ) ); + + searchCluster$ = createEffect(() => + this.actions$.pipe( + ofType(ClusterSummaryActions.searchCluster), + map((action) => action.request), + concatLatestFrom(() => + this.store.select(selectClusterSummary).pipe( + isDefinedAndNotNull(), + filter((clusterSummary) => clusterSummary.connectedToCluster) + ) + ), + switchMap(([request]) => { + return from(this.clusterService.searchCluster(request.q)).pipe( + map((response) => + ClusterSummaryActions.searchClusterSuccess({ + response: response + }) + ), + catchError((errorResponse: HttpErrorResponse) => + of(ErrorActions.snackBarError({ error: errorResponse.error })) + ) + ); + }) + ) + ); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.reducer.ts index a7c66b9712..d6fa003f09 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.reducer.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.reducer.ts @@ -18,14 +18,16 @@ import { createReducer, on } from '@ngrx/store'; import { ClusterSummaryState } from './index'; import { - clusterSummaryApiError, clearClusterSummaryApiError, + clusterSummaryApiError, loadClusterSummary, - loadClusterSummarySuccess + loadClusterSummarySuccess, + searchClusterSuccess } from './cluster-summary.actions'; export const initialState: ClusterSummaryState = { clusterSummary: null, + searchResults: null, error: null, status: 'pending' }; @@ -51,5 +53,9 @@ export const clusterSummaryReducer = createReducer( ...state, error: null, status: 'pending' as const + })), + on(searchClusterSuccess, (state, { response }) => ({ + ...state, + searchResults: response })) ); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.selectors.ts index 634593f253..58afb3a3d7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.selectors.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/cluster-summary.selectors.ts @@ -24,3 +24,8 @@ export const selectClusterSummary = createSelector( selectClusterSummaryState, (state: ClusterSummaryState) => state.clusterSummary ); + +export const selectClusterSearchResults = createSelector( + selectClusterSummaryState, + (state: ClusterSummaryState) => state.searchResults +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/index.ts index 09ee6b0bb2..7e90e99ae2 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/cluster-summary/index.ts @@ -40,6 +40,11 @@ export interface ClusterSearchResults { export interface ClusterSummaryState { clusterSummary: ClusterSummary | null; + searchResults: ClusterSearchResults | null; error: string | null; status: 'pending' | 'loading' | 'error' | 'success'; } + +export interface ClusterSearchRequest { + q?: string; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/_component-context.component-theme.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/_component-context.component-theme.scss new file mode 100644 index 0000000000..10194ed036 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/_component-context.component-theme.scss @@ -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. + */ + +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin nifi-theme($material-theme, $canvas-theme) { + // Get the color config from the theme. + $color-config: mat.get-color-config($material-theme); + $canvas-color-config: mat.get-color-config($canvas-theme); + + // Get the color palette from the color-config. + $primary-palette: map.get($color-config, 'primary'); + $accent-palette: map.get($color-config, 'accent'); + $warn-palette: map.get($color-config, 'warn'); + $canvas-primary-palette: map.get($canvas-color-config, 'primary'); + + // Get hues from palette + $primary-palette-700: mat.get-color-from-palette($primary-palette, 700); + $accent-palette-A400: mat.get-color-from-palette($accent-palette, 'A400'); + $warn-palette-A200: mat.get-color-from-palette($warn-palette, 'A200'); + $canvas-primary-palette-A200: mat.get-color-from-palette($canvas-primary-palette, 'A200'); + + .component-context { + .fa, + .icon { + color: $accent-palette-A400; + } + + .component-context-logo { + .icon { + color: $warn-palette-A200; + } + } + + .component-context-name { + color: $canvas-primary-palette-A200; + } + + .component-context-type { + color: $primary-palette-700; + } + + .component-context-id { + color: $warn-palette-A200; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.html new file mode 100644 index 0000000000..439e668617 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.html @@ -0,0 +1,31 @@ + + +
+
+ +
+
{{ name }}
+
{{ type | componentTypeName }}
+
+
+ @if (id) { +
{{ id }}
+ } +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.scss new file mode 100644 index 0000000000..5b9f87bf59 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.scss @@ -0,0 +1,41 @@ +/*! + * 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. + */ +.component-context { + .component-context-logo { + text-align: start; + + .icon { + font-size: 32px; + } + } + + .component-context-name { + font-size: 15px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + + .component-context-type { + font-size: 12px; + line-height: 16px; + } + + .component-context-id { + font-size: 12px; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.spec.ts new file mode 100644 index 0000000000..51ae4b3490 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.spec.ts @@ -0,0 +1,39 @@ +/* + * 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 { ComponentContext } from './component-context.component'; + +describe('ComponentContext', () => { + let component: ComponentContext; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ComponentContext] + }).compileComponents(); + + fixture = TestBed.createComponent(ComponentContext); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.ts new file mode 100644 index 0000000000..fb327f774d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/component-context/component-context.component.ts @@ -0,0 +1,67 @@ +/* + * 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, Input } from '@angular/core'; +import { ComponentType } from '../../../state/shared'; +import { ComponentTypeNamePipe } from '../../../pipes/component-type-name.pipe'; + +@Component({ + selector: 'component-context', + standalone: true, + imports: [ComponentTypeNamePipe], + templateUrl: './component-context.component.html', + styleUrl: './component-context.component.scss' +}) +export class ComponentContext { + private _componentType: ComponentType = ComponentType.Processor; + componentIconClass: string = ''; + + @Input() set type(type: ComponentType) { + this._componentType = type; + this.componentIconClass = this.getIconClassName(type); + } + + get type(): ComponentType { + return this._componentType; + } + + @Input() id: string | null = null; + @Input() name: string = ''; + + private getIconClassName(type: ComponentType) { + switch (type) { + case ComponentType.Connection: + return 'icon-connect'; + case ComponentType.Processor: + return 'icon-processor'; + case ComponentType.OutputPort: + return 'icon-port-out'; + case ComponentType.InputPort: + return 'icon-port-in'; + case ComponentType.ProcessGroup: + return 'icon-group'; + case ComponentType.Funnel: + return 'icon-funnel'; + case ComponentType.Label: + return 'icon-label'; + case ComponentType.RemoteProcessGroup: + return 'icon-group-remote'; + default: + return 'icon-connect'; + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/_listing-table-theme.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/_listing-table-theme.scss index 5f25862957..bcc5f6a9f6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/_listing-table-theme.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/assets/styles/_listing-table-theme.scss @@ -54,6 +54,7 @@ th { background-color: $primary-palette-500 !important; color: $canvas-primary-palette-900; + user-select: none; } tr:hover { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/styles.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/styles.scss index 524caae52f..63e2591dec 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/styles.scss +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/styles.scss @@ -62,6 +62,8 @@ @use 'app/ui/common/status-history/status-history.component-theme' as status-history; @use 'app/ui/common/tooltips/property-hint-tip/property-hint-tip.component-theme' as property-hint-tip; @use 'app/ui/common/provenance-event-dialog/provenance-event-dialog.component-theme' as provenance-event-dialog; +@use 'app/pages/summary/ui/processor-status-listing/processor-status-table/processor-status-table.component-theme' as processor-status-table; +@use 'app/ui/common/component-context/component-context.component-theme' as component-context; // Plus imports for other components in your app. @use 'roboto-fontface/css/roboto/roboto-fontface.css'; @@ -513,6 +515,8 @@ $appFontPath: '~roboto-fontface/fonts'; @include status-history.nifi-theme($material-theme-light, $nifi-canvas-theme-light); @include property-hint-tip.nifi-theme($material-theme-light); @include provenance-event-dialog.nifi-theme($material-theme-light); +@include processor-status-table.nifi-theme($material-theme-light); +@include component-context.nifi-theme($material-theme-light, $nifi-canvas-theme-light); .dark-theme { // Include the dark theme color styles. @@ -563,4 +567,6 @@ $appFontPath: '~roboto-fontface/fonts'; @include status-history.nifi-theme($material-theme-dark, $nifi-canvas-theme-dark); @include property-hint-tip.nifi-theme($material-theme-dark); @include provenance-event-dialog.nifi-theme($material-theme-dark); + @include processor-status-table.nifi-theme($material-theme-dark); + @include component-context.nifi-theme($material-theme-dark, $nifi-canvas-theme-dark); }