This is an automated email from the ASF dual-hosted git repository.
rfellows pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new aee5e61cd2 NIFI-15096: Also consider system diagnostics loading status
in the cl… (#10423)
aee5e61cd2 is described below
commit aee5e61cd23a0079c0a5f93c73c03f98791a5556
Author: Matt Gilman <[email protected]>
AuthorDate: Wed Oct 15 17:16:36 2025 -0400
NIFI-15096: Also consider system diagnostics loading status in the cl…
(#10423)
* NIFI-15096: Also consider system diagnostics loading status in the
cluster listing spinner.
* NIFI-15096: Considering the loaded timestamp for the system diagnostics
for skeleton loaders on the tables in the cluster listing.
* NIFI-15096: Using a computed signal for initialLoading.
This closes #10423
---
.../pages/cluster/feature/cluster.component.html | 6 ++-
.../cluster/feature/cluster.component.spec.ts | 5 +-
.../app/pages/cluster/feature/cluster.component.ts | 2 +
.../cluster-content-storage-listing.component.html | 2 +-
...uster-content-storage-listing.component.spec.ts | 56 ++++++++++++++++++++-
.../cluster-content-storage-listing.component.ts | 17 +++++--
...luster-flow-file-storage-listing.component.html | 2 +-
...ter-flow-file-storage-listing.component.spec.ts | 57 ++++++++++++++++++++--
.../cluster-flow-file-storage-listing.component.ts | 17 +++++--
.../cluster-jvm-listing.component.html | 2 +-
.../cluster-jvm-listing.component.spec.ts | 57 ++++++++++++++++++++--
.../cluster-jvm-listing.component.ts | 17 +++++--
...uster-provenance-storage-listing.component.html | 2 +-
...er-provenance-storage-listing.component.spec.ts | 57 ++++++++++++++++++++--
...cluster-provenance-storage-listing.component.ts | 17 +++++--
.../cluster-system-listing.component.html | 2 +-
.../cluster-system-listing.component.spec.ts | 57 ++++++++++++++++++++--
.../cluster-system-listing.component.ts | 17 +++++--
.../cluster-version-listing.component.html | 2 +-
.../cluster-version-listing.component.spec.ts | 57 ++++++++++++++++++++--
.../cluster-version-listing.component.ts | 17 +++++--
21 files changed, 413 insertions(+), 55 deletions(-)
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
index e26b27142e..4ddef40ebd 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.html
@@ -50,7 +50,11 @@
<div class="flex justify-between align-middle">
<div class="text-sm flex items-center gap-x-2">
<button mat-icon-button class="primary-icon-button"
(click)="refresh()">
- <i class="fa fa-refresh"
[class.fa-spin]="listingStatus() === 'loading'"></i>
+ <i
+ class="fa fa-refresh"
+ [class.fa-spin]="
+ listingStatus() === 'loading' ||
systemDiagnosticsStatus() === 'loading'
+ "></i>
</button>
<div>Last updated:</div>
<div class="tertiary-color font-medium">{{
loadedTimestamp() }}</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
index 8dd5f41444..d892cf81e5 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.spec.ts
@@ -33,6 +33,8 @@ import { initialState as initialErrorState } from
'../../../state/error/error.re
import { errorFeatureKey } from '../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../state/system-diagnostics';
describe('Cluster', () => {
let component: Cluster;
@@ -44,7 +46,8 @@ describe('Cluster', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
TestBed.configureTestingModule({
declarations: [Cluster],
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
index 787a696a75..1fe2a43451 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/feature/cluster.component.ts
@@ -34,6 +34,7 @@ import { takeUntilDestroyed } from
'@angular/core/rxjs-interop';
import { selectCurrentRoute } from '@nifi/shared';
import { resetSystemDiagnostics } from
'../../../state/system-diagnostics/system-diagnostics.actions';
import { ErrorContextKey } from '../../../state/error';
+import { selectSystemDiagnosticsStatus } from
'../../../state/system-diagnostics/system-diagnostics.selectors';
interface TabLink {
label: string;
@@ -62,6 +63,7 @@ export class Cluster implements OnInit, OnDestroy {
];
listingStatus = this.store.selectSignal(selectClusterListingStatus);
+ systemDiagnosticsStatus =
this.store.selectSignal(selectSystemDiagnosticsStatus);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
currentUser$ = this.store.select(selectCurrentUser);
currentRoute = this.store.selectSignal(selectCurrentRoute);
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
index 66feaf7f0e..9ec108252d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
index 30ae263cce..c0352111bd 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.spec.ts
@@ -22,15 +22,23 @@ import { clusterListingFeatureKey } from
'../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
+import { MockStore } from '@ngrx/store/testing';
describe('ClusterContentStorageListing', () => {
let component: ClusterContentStorageListing;
let fixture: ComponentFixture<ClusterContentStorageListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +46,8 @@ describe('ClusterContentStorageListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
imports: [ClusterContentStorageListing],
@@ -55,6 +64,7 @@ describe('ClusterContentStorageListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterContentStorageListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -63,4 +73,46 @@ describe('ClusterContentStorageListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
index 57e4e3c2ca..151fae204b 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-content-storage-listing/cluster-content-storage-listing.component.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { RepositoryStorageTable } from
'../common/repository-storage-table/repository-storage-table.component';
@@ -25,7 +25,10 @@ import {
selectClusterNodeIdFromRoute,
selectClusterStorageRepositoryIdFromRoute
} from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { isDefinedAndNotNull } from '@nifi/shared';
import { map } from 'rxjs';
import { ClusterNodeRepositoryStorageUsage } from
'../../../../state/system-diagnostics';
@@ -37,6 +40,7 @@ import {
selectContentStorageNode
} from '../../state/cluster-listing/cluster-listing.actions';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-content-storage-listing',
@@ -48,6 +52,7 @@ export class ClusterContentStorageListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
selectedClusterRepoId =
this.store.selectSignal(selectClusterStorageRepositoryIdFromRoute);
@@ -70,9 +75,11 @@ export class ClusterContentStorageListing {
})
);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
this.store.dispatch(
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
index 7276ea2ca4..9637602936 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
index 2b081a9f53..d3cc05d825 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterFlowFileStorageListing } from
'./cluster-flow-file-storage-listi
import { clusterListingFeatureKey } from '../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
describe('ClusterFlowFileStorageListing', () => {
let component: ClusterFlowFileStorageListing;
let fixture: ComponentFixture<ClusterFlowFileStorageListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterFlowFileStorageListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
imports: [ClusterFlowFileStorageListing],
@@ -55,6 +63,7 @@ describe('ClusterFlowFileStorageListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterFlowFileStorageListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -63,4 +72,46 @@ describe('ClusterFlowFileStorageListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
index 77db7efe96..4a3df3b9e3 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-flow-file-storage-listing/cluster-flow-file-storage-listing.component.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import { RepositoryStorageTable } from
'../common/repository-storage-table/repository-storage-table.component';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import {
@@ -23,7 +23,10 @@ import {
selectClusterListingStatus,
selectClusterNodeIdFromRoute
} from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
@@ -36,6 +39,7 @@ import {
selectFlowFileStorageNode
} from '../../state/cluster-listing/cluster-listing.actions';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-flow-file-storage-listing',
@@ -47,6 +51,7 @@ export class ClusterFlowFileStorageListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
components$ = this.store.select(selectSystemNodeSnapshots).pipe(
@@ -64,9 +69,11 @@ export class ClusterFlowFileStorageListing {
})
);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
this.store.dispatch(selectFlowFileStorageNode({ request: { id:
node.nodeId } }));
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
index ea7c03025c..5ec125e6fd 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
index 3775a04b17..f9f9c4d67d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterJvmListing } from
'./cluster-jvm-listing.component';
import { clusterListingFeatureKey } from '../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
describe('ClusterJvmListing', () => {
let component: ClusterJvmListing;
let fixture: ComponentFixture<ClusterJvmListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterJvmListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterJvmListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterJvmListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterJvmListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
index 6715a2fb1a..f0478b8979 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-jvm-listing/cluster-jvm-listing.component.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { ClusterJvmTable } from
'./cluster-jvm-table/cluster-jvm-table.component';
import {
@@ -23,12 +23,16 @@ import {
selectClusterListingStatus,
selectClusterNodeIdFromRoute
} from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { NodeSnapshot } from '../../../../state/system-diagnostics';
import { clearJvmNodeSelection, selectJvmNode } from
'../../state/cluster-listing/cluster-listing.actions';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-jvm-listing',
@@ -40,13 +44,16 @@ export class ClusterJvmListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
nodes = this.store.selectSignal(selectSystemNodeSnapshots);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectNode(node: NodeSnapshot): void {
this.store.dispatch(selectJvmNode({ request: { id: node.nodeId } }));
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
index 66feaf7f0e..9ec108252d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
index 04ebe65f10..a4196c4a8e 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterProvenanceStorageListing } from
'./cluster-provenance-storage-li
import { clusterListingFeatureKey } from '../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
describe('ClusterProvenanceStorageListing', () => {
let component: ClusterProvenanceStorageListing;
let fixture: ComponentFixture<ClusterProvenanceStorageListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterProvenanceStorageListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterProvenanceStorageListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterProvenanceStorageListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterProvenanceStorageListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
index 334b7bca58..f47c8896ef 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-provenance-storage-listing/cluster-provenance-storage-listing.component.ts
@@ -15,14 +15,17 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import {
selectClusterListingLoadedTimestamp,
selectClusterListingStatus,
selectClusterNodeIdFromRoute,
selectClusterStorageRepositoryIdFromRoute
} from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { isDefinedAndNotNull } from '@nifi/shared';
import { map } from 'rxjs';
import { ClusterNodeRepositoryStorageUsage } from
'../../../../state/system-diagnostics';
@@ -37,6 +40,7 @@ import { AsyncPipe } from '@angular/common';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { RepositoryStorageTable } from
'../common/repository-storage-table/repository-storage-table.component';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-provenance-storage-listing',
@@ -48,6 +52,7 @@ export class ClusterProvenanceStorageListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
selectedClusterRepoId =
this.store.selectSignal(selectClusterStorageRepositoryIdFromRoute);
@@ -70,9 +75,11 @@ export class ClusterProvenanceStorageListing {
})
);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectStorageNode(node: ClusterNodeRepositoryStorageUsage): void {
this.store.dispatch(
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
index aba7db4deb..2b797adf9d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
index a388c959e8..b8ff559b10 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterSystemListing } from
'./cluster-system-listing.component';
import { clusterListingFeatureKey } from '../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
describe('ClusterSystemListing', () => {
let component: ClusterSystemListing;
let fixture: ComponentFixture<ClusterSystemListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterSystemListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
imports: [ClusterSystemListing],
@@ -55,6 +63,7 @@ describe('ClusterSystemListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterSystemListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -63,4 +72,46 @@ describe('ClusterSystemListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
index 8db35f7683..ebd7f66a60 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-system-listing/cluster-system-listing.component.ts
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import {
selectClusterListingLoadedTimestamp,
selectClusterListingStatus,
@@ -25,10 +25,14 @@ import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { ClusterSystemTable } from
'./cluster-system-table/cluster-system-table.component';
import { clearSystemNodeSelection, selectSystemNode } from
'../../state/cluster-listing/cluster-listing.actions';
import { NodeSnapshot } from '../../../../state/system-diagnostics';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-system-listing',
@@ -40,13 +44,16 @@ export class ClusterSystemListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
nodes = this.store.selectSignal(selectSystemNodeSnapshots);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectNode(node: NodeSnapshot): void {
this.store.dispatch(selectSystemNode({ request: { id: node.nodeId }
}));
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
index 96e1007bf4..1954fd9bd5 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.html
@@ -16,7 +16,7 @@
-->
<ng-container>
- @if (isInitialLoading(loadedTimestamp())) {
+ @if (isInitialLoading()) {
<div>
<ngx-skeleton-loader count="3"></ngx-skeleton-loader>
</div>
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
index de1a7cea75..b8f4ae5c05 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.spec.ts
@@ -21,16 +21,23 @@ import { ClusterVersionListing } from
'./cluster-version-listing.component';
import { clusterListingFeatureKey } from '../../state/cluster-listing';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
import { clusterFeatureKey } from '../../state';
-import { provideMockStore } from '@ngrx/store/testing';
-import { selectClusterListing } from
'../../state/cluster-listing/cluster-listing.selectors';
+import { provideMockStore, MockStore } from '@ngrx/store/testing';
+import {
+ selectClusterListing,
+ selectClusterListingLoadedTimestamp
+} from '../../state/cluster-listing/cluster-listing.selectors';
+import { selectSystemDiagnosticsLoadedTimestamp } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
import { initialState as initialErrorState } from
'../../../../state/error/error.reducer';
import { errorFeatureKey } from '../../../../state/error';
import { initialState as initialCurrentUserState } from
'../../../../state/current-user/current-user.reducer';
import { currentUserFeatureKey } from '../../../../state/current-user';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
+import { systemDiagnosticsFeatureKey } from
'../../../../state/system-diagnostics';
describe('ClusterVersionListing', () => {
let component: ClusterVersionListing;
let fixture: ComponentFixture<ClusterVersionListing>;
+ let store: MockStore;
beforeEach(async () => {
const initialState = {
@@ -38,7 +45,8 @@ describe('ClusterVersionListing', () => {
[currentUserFeatureKey]: initialCurrentUserState,
[clusterFeatureKey]: {
[clusterListingFeatureKey]: initialClusterState
- }
+ },
+ [systemDiagnosticsFeatureKey]: initialSystemDiagnosticsState
};
await TestBed.configureTestingModule({
@@ -56,6 +64,7 @@ describe('ClusterVersionListing', () => {
]
}).compileComponents();
+ store = TestBed.inject(MockStore);
fixture = TestBed.createComponent(ClusterVersionListing);
component = fixture.componentInstance;
fixture.detectChanges();
@@ -64,4 +73,46 @@ describe('ClusterVersionListing', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ describe('isInitialLoading', () => {
+ it('should return true when both timestamps are empty (initial
state)', () => {
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when cluster timestamp is empty but system
diagnostics has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp, '');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:00 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return true when system diagnostics timestamp is empty but
cluster has loaded', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp, '');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(true);
+ });
+
+ it('should return false when both timestamps are populated', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+
+ it('should return false when data has been loaded and user triggers a
refresh', () => {
+ store.overrideSelector(selectClusterListingLoadedTimestamp,
'10:30:00 UTC');
+ store.overrideSelector(selectSystemDiagnosticsLoadedTimestamp,
'10:30:05 UTC');
+ store.refreshState();
+ fixture.detectChanges();
+
+ expect(component.isInitialLoading()).toBe(false);
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
index eec2279d9e..adfeabd14f 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/cluster/ui/cluster-version-listing/cluster-version-listing.component.ts
@@ -15,13 +15,16 @@
* limitations under the License.
*/
-import { Component, inject } from '@angular/core';
+import { Component, computed, inject } from '@angular/core';
import {
selectClusterListingLoadedTimestamp,
selectClusterListingStatus,
selectClusterNodeIdFromRoute
} from '../../state/cluster-listing/cluster-listing.selectors';
-import { selectSystemNodeSnapshots } from
'../../../../state/system-diagnostics/system-diagnostics.selectors';
+import {
+ selectSystemDiagnosticsLoadedTimestamp,
+ selectSystemNodeSnapshots
+} from '../../../../state/system-diagnostics/system-diagnostics.selectors';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../state';
import { initialClusterState } from
'../../state/cluster-listing/cluster-listing.reducer';
@@ -29,6 +32,7 @@ import { NodeSnapshot } from
'../../../../state/system-diagnostics';
import { clearVersionsNodeSelection, selectVersionNode } from
'../../state/cluster-listing/cluster-listing.actions';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { ClusterVersionTable } from
'./cluster-version-table/cluster-version-table.component';
+import { initialSystemDiagnosticsState } from
'../../../../state/system-diagnostics/system-diagnostics.reducer';
@Component({
selector: 'cluster-version-listing',
@@ -40,13 +44,16 @@ export class ClusterVersionListing {
private store = inject<Store<NiFiState>>(Store);
loadedTimestamp =
this.store.selectSignal(selectClusterListingLoadedTimestamp);
+ systemDiagnosticsLoadedTimestamp =
this.store.selectSignal(selectSystemDiagnosticsLoadedTimestamp);
listingStatus = this.store.selectSignal(selectClusterListingStatus);
selectedClusterNodeId =
this.store.selectSignal(selectClusterNodeIdFromRoute);
nodes = this.store.selectSignal(selectSystemNodeSnapshots);
- isInitialLoading(loadedTimestamp: string): boolean {
- return loadedTimestamp == initialClusterState.loadedTimestamp;
- }
+ isInitialLoading = computed(
+ () =>
+ this.loadedTimestamp() == initialClusterState.loadedTimestamp ||
+ this.systemDiagnosticsLoadedTimestamp() ==
initialSystemDiagnosticsState.loadedTimestamp
+ );
selectNode(node: NodeSnapshot): void {
this.store.dispatch(selectVersionNode({ request: { id: node.nodeId }
}));