This is an automated email from the ASF dual-hosted git repository.
scottyaslan 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 25d58327df NIFI-15080 - Display bulletin severity (color) in listing
tables (#10407)
25d58327df is described below
commit 25d58327df65fb14ee887f90bfdbb7fb2f3ff0e5
Author: Rob Fellows <[email protected]>
AuthorDate: Fri Oct 10 13:38:38 2025 -0400
NIFI-15080 - Display bulletin severity (color) in listing tables (#10407)
---
.../flow-designer/service/canvas-utils.service.ts | 33 +-
.../header/flow-status/flow-status.component.ts | 7 +-
.../edit-processor/edit-processor.component.ts | 2 +-
.../flow-analysis-rule-table.component.html | 3 +-
.../flow-analysis-rule-table.component.ts | 4 +
.../registry-client-table.component.html | 3 +-
.../registry-client-table.component.ts | 16 +-
.../reporting-task-table.component.html | 3 +-
.../reporting-task-table.component.ts | 4 +
.../controller-service-references.component.html | 6 +-
.../controller-service-references.component.ts | 4 +
.../controller-service-table.component.html | 3 +-
.../controller-service-table.component.ts | 4 +
.../parameter-references.component.html | 6 +-
.../parameter-references.component.ts | 4 +
.../src/services/nifi-common.service.spec.ts | 401 +++++++++++++++++++++
.../shared/src/services/nifi-common.service.ts | 67 +++-
17 files changed, 523 insertions(+), 47 deletions(-)
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/service/canvas-utils.service.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/service/canvas-utils.service.ts
index de006e6e1f..47aa26ccf6 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/service/canvas-utils.service.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/service/canvas-utils.service.ts
@@ -1279,37 +1279,6 @@ export class CanvasUtils {
selection.on('mouseenter', null).on('mouseleave', null);
}
- private getHigherSeverityBulletinLevel(left: BulletinEntity, right:
BulletinEntity): BulletinEntity {
- const bulletinSeverityMap: { [key: string]: number } = {
- TRACE: 0,
- DEBUG: 1,
- INFO: 2,
- WARNING: 3,
- ERROR: 4
- };
- let mappedLeft = 0;
- let mappedRight = 0;
- if (left.bulletin) {
- mappedLeft =
bulletinSeverityMap[left.bulletin.level.toUpperCase()] || 0;
- }
- if (right.bulletin) {
- mappedRight =
bulletinSeverityMap[right.bulletin.level.toUpperCase()] || 0;
- }
- return mappedLeft >= mappedRight ? left : right;
- }
-
- public getMostSevereBulletin(bulletins: BulletinEntity[]): BulletinEntity
| null {
- if (bulletins && bulletins.length > 0) {
- const mostSevere = bulletins.reduce((previous, current) => {
- return this.getHigherSeverityBulletinLevel(previous, current);
- });
- if (mostSevere.bulletin) {
- return mostSevere;
- }
- }
- return null;
- }
-
private resetBulletin(selection: any) {
// reset the bulletin icon/background
selection.select('text.bulletin-icon').style('visibility', 'hidden');
@@ -1335,7 +1304,7 @@ export class CanvasUtils {
this.resetBulletin(selection);
} else {
// determine the most severe of the bulletins
- const mostSevere = this.getMostSevereBulletin(filteredBulletins);
+ const mostSevere =
this.nifiCommon.getMostSevereBulletin(filteredBulletins);
// add the proper class to indicate the most severe bulletin
if (mostSevere) {
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/header/flow-status/flow-status.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/header/flow-status/flow-status.component.ts
index 1ff82eb87c..942c0dd0fa 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/header/flow-status/flow-status.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/header/flow-status/flow-status.component.ts
@@ -22,7 +22,7 @@ import { BulletinsTip } from
'../../../../../../ui/common/tooltips/bulletins-tip
import { BulletinsTipInput } from '../../../../../../state/shared';
import { Search } from '../search/search.component';
-import { BulletinEntity, NifiTooltipDirective, Storage } from '@nifi/shared';
+import { BulletinEntity, NifiTooltipDirective, Storage, NiFiCommon } from
'@nifi/shared';
import { ClusterSummary } from '../../../../../../state/cluster-summary';
import { ConnectedPosition } from '@angular/cdk/overlay';
import { FlowAnalysisState } from '../../../../state/flow-analysis';
@@ -30,7 +30,6 @@ import { CommonModule } from '@angular/common';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../../../../../state';
import { setFlowAnalysisOpen } from '../../../../state/flow/flow.actions';
-import { CanvasUtils } from '../../../../service/canvas-utils.service';
@Component({
selector: 'flow-status',
@@ -41,7 +40,7 @@ import { CanvasUtils } from
'../../../../service/canvas-utils.service';
export class FlowStatus {
private store = inject<Store<NiFiState>>(Store);
private storage = inject(Storage);
- private canvasUtils = inject(CanvasUtils);
+ private nifiCommon = inject(NiFiCommon);
private static readonly FLOW_ANALYSIS_VISIBILITY_KEY: string =
'flow-analysis-visibility';
private static readonly FLOW_ANALYSIS_KEY: string = 'flow-analysis';
@@ -172,7 +171,7 @@ export class FlowStatus {
getMostSevereBulletinLevel(): string | null {
// determine the most severe of the bulletins
- const mostSevere =
this.canvasUtils.getMostSevereBulletin(this.filteredBulletins);
+ const mostSevere =
this.nifiCommon.getMostSevereBulletin(this.filteredBulletins);
return mostSevere ? mostSevere.bulletin.level.toLowerCase() : null;
}
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts
index 8b3aa4ca55..f5f24ee199 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/ui/canvas/items/processor/edit-processor/edit-processor.component.ts
@@ -487,7 +487,7 @@ export class EditProcessor extends TabbedDialog {
getMostSevereBulletinLevel(): string | null {
// determine the most severe of the bulletins
- const mostSevere =
this.canvasUtils.getMostSevereBulletin(this.bulletins);
+ const mostSevere =
this.nifiCommon.getMostSevereBulletin(this.bulletins);
return mostSevere ? mostSevere.bulletin.level.toLowerCase() : null;
}
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
index 2798ffbe9b..6be9901cb3 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.html
@@ -49,7 +49,8 @@
}
@if (hasBulletins(item)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+ [ngClass]="getBulletinSeverityClass(item)"
[delayClose]="true"
nifiTooltip
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
index ff19a62f7c..448dd86c9e 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/flow-analysis-rules/flow-analysis-rule-table/flow-analysis-rule-table.component.ts
@@ -156,6 +156,10 @@ export class FlowAnalysisRuleTable {
};
}
+ getBulletinSeverityClass(entity: FlowAnalysisRuleEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins);
+ }
+
getStateIcon(entity: FlowAnalysisRuleEntity): string {
if (entity.status.validationStatus === 'VALIDATING') {
return 'validating neutral-color fa fa-spin fa-circle-o-notch';
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.html
index a02826164f..5909959cde 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.html
@@ -40,7 +40,8 @@
}
@if (hasBulletins(item)) {
<div
- class="mr-3 pointer fa fa-sticky-note-o
primary-color"
+ class="mr-3 pointer fa fa-sticky-note"
+ [ngClass]="getBulletinSeverityClass(item)"
nifiTooltip
[delayClose]="true"
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.ts
index 6b8ff63728..fc5ff42c07 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/registry-clients/registry-client-table/registry-client-table.component.ts
@@ -18,6 +18,7 @@
import { Component, EventEmitter, Input, Output, inject } from '@angular/core';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatSortModule, Sort } from '@angular/material/sort';
+import { NgClass } from '@angular/common';
import { ReportingTaskEntity } from '../../../state/reporting-tasks';
import { TextTip, NiFiCommon, NifiTooltipDirective } from '@nifi/shared';
import { BulletinsTip } from
'../../../../../ui/common/tooltips/bulletins-tip/bulletins-tip.component';
@@ -30,7 +31,16 @@ import { MatMenu, MatMenuItem, MatMenuTrigger } from
'@angular/material/menu';
selector: 'registry-client-table',
templateUrl: './registry-client-table.component.html',
styleUrls: ['./registry-client-table.component.scss'],
- imports: [MatTableModule, MatSortModule, NifiTooltipDirective,
MatIconButton, MatMenuTrigger, MatMenu, MatMenuItem]
+ imports: [
+ MatTableModule,
+ MatSortModule,
+ NgClass,
+ NifiTooltipDirective,
+ MatIconButton,
+ MatMenuTrigger,
+ MatMenu,
+ MatMenuItem
+ ]
})
export class RegistryClientTable {
private nifiCommon = inject(NiFiCommon);
@@ -89,6 +99,10 @@ export class RegistryClientTable {
};
}
+ getBulletinSeverityClass(entity: RegistryClientEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins ||
[]);
+ }
+
formatType(entity: RegistryClientEntity): string {
return this.nifiCommon.formatType(entity.component);
}
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
index 9bdcba1cab..a9858f209d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.html
@@ -49,7 +49,8 @@
}
@if (hasBulletins(item)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+ [ngClass]="getBulletinSeverityClass(item)"
[delayClose]="true"
nifiTooltip
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
index 19e741a636..b0279e439d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts
@@ -125,6 +125,10 @@ export class ReportingTaskTable {
};
}
+ getBulletinSeverityClass(entity: ReportingTaskEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins);
+ }
+
getStateIcon(entity: ReportingTaskEntity): string {
if (entity.status.validationStatus === 'VALIDATING') {
return 'validating neutral-color fa fa-spin fa-circle-o-notch';
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.html
index dbe6edbc28..092c74618d 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.html
@@ -107,7 +107,8 @@
}
@if (hasBulletins(reference)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+
[ngClass]="getBulletinSeverityClass(reference)"
nifiTooltip
[tooltipComponentType]="BulletinsTip"
[tooltipInputData]="getBulletinsTipData(reference)"
@@ -150,7 +151,8 @@
@if (hasBulletins(service)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+
[ngClass]="getBulletinSeverityClass(service)"
nifiTooltip
[delayClose]="true"
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.ts
index c6a5893cc8..9039a65ea5 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-references/controller-service-references.component.ts
@@ -113,6 +113,10 @@ export class ControllerServiceReferences {
};
}
+ getBulletinSeverityClass(entity:
ControllerServiceReferencingComponentEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins);
+ }
+
hasActiveThreads(reference: ControllerServiceReferencingComponent):
boolean {
return reference.activeThreadCount != null &&
reference.activeThreadCount > 0;
}
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
index 45c69f605c..07b99dcd51 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.html
@@ -49,7 +49,8 @@
}
@if (hasBulletins(item)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+ [ngClass]="getBulletinSeverityClass(item)"
nifiTooltip
[delayClose]="true"
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
index 1ae3d5dfe0..63e300580e 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts
@@ -138,6 +138,10 @@ export class ControllerServiceTable {
};
}
+ getBulletinSeverityClass(entity: ControllerServiceEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins);
+ }
+
getStateIcon(entity: ControllerServiceEntity): string {
if (entity.status.validationStatus === 'VALIDATING') {
return 'validating neutral-color fa fa-spin fa-circle-o-notch';
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.html
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.html
index 325d93cc78..16ed977b03 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.html
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.html
@@ -75,7 +75,8 @@
}
@if (hasBulletins(reference)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+
[ngClass]="getBulletinSeverityClass(reference)"
nifiTooltip
[delayClose]="true"
[tooltipComponentType]="BulletinsTip"
@@ -116,7 +117,8 @@
}
@if (hasBulletins(service)) {
<div
- class="pointer fa fa-sticky-note-o
primary-color"
+ class="pointer fa fa-sticky-note"
+
[ngClass]="getBulletinSeverityClass(service)"
nifiTooltip
[delayClose]="true"
[tooltipComponentType]="BulletinsTip"
diff --git
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.ts
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.ts
index c4394a8d8f..c9692168aa 100644
---
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.ts
+++
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/parameter-references/parameter-references.component.ts
@@ -147,6 +147,10 @@ export class ParameterReferences {
};
}
+ getBulletinSeverityClass(entity: AffectedComponentEntity): string {
+ return this.nifiCommon.getBulletinSeverityClass(entity.bulletins);
+ }
+
hasActiveThreads(reference: AffectedComponent): boolean {
return reference.activeThreadCount != null &&
reference.activeThreadCount > 0;
}
diff --git
a/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.spec.ts
b/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.spec.ts
index c4e9a6b04d..af3140611f 100644
---
a/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.spec.ts
+++
b/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.spec.ts
@@ -97,4 +97,405 @@ describe('Common', () => {
});
});
});
+
+ describe('getMostSevereBulletin', () => {
+ it('should return null when no bulletins provided', () => {
+ const result = service.getMostSevereBulletin([]);
+ expect(result).toBeNull();
+ });
+
+ it('should return null when bulletins is null', () => {
+ const result = service.getMostSevereBulletin(null as any);
+ expect(result).toBeNull();
+ });
+
+ it('should return null when bulletins is undefined', () => {
+ const result = service.getMostSevereBulletin(undefined as any);
+ expect(result).toBeNull();
+ });
+
+ it('should return the bulletin when only one bulletin provided', () =>
{
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'ERROR',
+ message: 'Error message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getMostSevereBulletin(bulletins);
+ expect(result).toEqual(bulletins[0]);
+ });
+
+ it('should return ERROR bulletin when multiple severities present', ()
=> {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'INFO',
+ message: 'Info message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ },
+ {
+ id: 2,
+ canRead: true,
+ sourceId: 'source2',
+ groupId: 'group2',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 2,
+ sourceId: 'source2',
+ groupId: 'group2',
+ category: 'test',
+ sourceName: 'Source 2',
+ sourceType: 'PROCESSOR',
+ level: 'ERROR',
+ message: 'Error message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getMostSevereBulletin(bulletins);
+ expect(result).toEqual(bulletins[1]);
+ });
+
+ it('should return WARNING bulletin when no ERROR present', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'INFO',
+ message: 'Info message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ },
+ {
+ id: 2,
+ canRead: true,
+ sourceId: 'source2',
+ groupId: 'group2',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 2,
+ sourceId: 'source2',
+ groupId: 'group2',
+ category: 'test',
+ sourceName: 'Source 2',
+ sourceType: 'PROCESSOR',
+ level: 'WARNING',
+ message: 'Warning message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getMostSevereBulletin(bulletins);
+ expect(result).toEqual(bulletins[1]);
+ });
+ });
+
+ describe('getBulletinSeverityClass', () => {
+ it('should return tertiary-color when no bulletins provided', () => {
+ const result = service.getBulletinSeverityClass([]);
+ expect(result).toBe('tertiary-color');
+ });
+
+ it('should return tertiary-color when bulletins is null', () => {
+ const result = service.getBulletinSeverityClass(null as any);
+ expect(result).toBe('tertiary-color');
+ });
+
+ it('should return error-color for error level bulletins', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'ERROR',
+ message: 'Error message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('error-color');
+ });
+
+ it('should return caution-color for warning level bulletins', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'WARNING',
+ message: 'Warning message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('caution-color');
+ });
+
+ it('should return caution-color for warn level bulletins (case
insensitive)', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'warn',
+ message: 'Warning message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('caution-color');
+ });
+
+ it('should return success-color-default for info level bulletins', ()
=> {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'INFO',
+ message: 'Info message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('success-color-default');
+ });
+
+ it('should return success-color-default for debug level bulletins', ()
=> {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'DEBUG',
+ message: 'Debug message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('success-color-default');
+ });
+
+ it('should return success-color-default for trace level bulletins', ()
=> {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'TRACE',
+ message: 'Trace message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('success-color-default');
+ });
+
+ it('should return success-color-default for unknown level bulletins',
() => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'UNKNOWN',
+ message: 'Unknown message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('success-color-default');
+ });
+
+ it('should return error-color when multiple bulletins with different
severities', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'INFO',
+ message: 'Info message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ },
+ {
+ id: 2,
+ canRead: true,
+ sourceId: 'source2',
+ groupId: 'group2',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 2,
+ sourceId: 'source2',
+ groupId: 'group2',
+ category: 'test',
+ sourceName: 'Source 2',
+ sourceType: 'PROCESSOR',
+ level: 'ERROR',
+ message: 'Error message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('error-color');
+ });
+
+ it('should return caution-color when multiple bulletins with warning
being most severe', () => {
+ const bulletins = [
+ {
+ id: 1,
+ canRead: true,
+ sourceId: 'source1',
+ groupId: 'group1',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 1,
+ sourceId: 'source1',
+ groupId: 'group1',
+ category: 'test',
+ sourceName: 'Source 1',
+ sourceType: 'PROCESSOR',
+ level: 'INFO',
+ message: 'Info message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ },
+ {
+ id: 2,
+ canRead: true,
+ sourceId: 'source2',
+ groupId: 'group2',
+ timestamp: '2023-01-01T00:00:00Z',
+ bulletin: {
+ id: 2,
+ sourceId: 'source2',
+ groupId: 'group2',
+ category: 'test',
+ sourceName: 'Source 2',
+ sourceType: 'PROCESSOR',
+ level: 'WARNING',
+ message: 'Warning message',
+ timestamp: '2023-01-01T00:00:00Z'
+ }
+ }
+ ];
+ const result = service.getBulletinSeverityClass(bulletins);
+ expect(result).toBe('caution-color');
+ });
+ });
});
diff --git
a/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.ts
b/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.ts
index c6a41dbe68..004de27d39 100644
---
a/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.ts
+++
b/nifi-frontend/src/main/frontend/libs/shared/src/services/nifi-common.service.ts
@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
-import { SelectOption } from '../types';
+import { SelectOption, BulletinEntity } from '../types';
@Injectable({
providedIn: 'root'
@@ -713,4 +713,69 @@ export class NiFiCommon {
public stripProtocol(url: string): string {
return this.substringAfterFirst(url, ':');
}
+
+ /**
+ * Determines the most severe bulletin from a list of bulletins.
+ * Severity order: ERROR > WARNING > INFO > DEBUG > TRACE
+ */
+ public getMostSevereBulletin(bulletins: BulletinEntity[]): BulletinEntity
| null {
+ if (bulletins && bulletins.length > 0) {
+ const mostSevere = bulletins.reduce((previous, current) => {
+ return this.getHigherSeverityBulletinLevel(previous, current);
+ });
+ if (mostSevere.bulletin) {
+ return mostSevere;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Helper method to determine which bulletin has higher severity.
+ * Uses numeric mapping for severity comparison.
+ */
+ private getHigherSeverityBulletinLevel(left: BulletinEntity, right:
BulletinEntity): BulletinEntity {
+ const bulletinSeverityMap: { [key: string]: number } = {
+ TRACE: 0,
+ DEBUG: 1,
+ INFO: 2,
+ WARNING: 3,
+ ERROR: 4
+ };
+ let mappedLeft = 0;
+ let mappedRight = 0;
+ if (left.bulletin) {
+ mappedLeft =
bulletinSeverityMap[left.bulletin.level.toUpperCase()] || 0;
+ }
+ if (right.bulletin) {
+ mappedRight =
bulletinSeverityMap[right.bulletin.level.toUpperCase()] || 0;
+ }
+
+ return mappedLeft >= mappedRight ? left : right;
+ }
+
+ /**
+ * Returns the appropriate CSS class for bulletin severity-based styling.
+ * @param bulletins Array of bulletin entities
+ * @returns CSS class name for severity-based styling
+ */
+ public getBulletinSeverityClass(bulletins: BulletinEntity[]): string {
+ const mostSevere = this.getMostSevereBulletin(bulletins);
+ if (!mostSevere) {
+ return 'tertiary-color';
+ }
+
+ switch (mostSevere.bulletin.level.toLowerCase()) {
+ case 'error':
+ return 'error-color';
+ case 'warn':
+ case 'warning':
+ return 'caution-color';
+ case 'info':
+ case 'debug':
+ case 'trace':
+ default:
+ return 'success-color-default';
+ }
+ }
}