Skip to content

Commit

Permalink
[Inventory][Hosts] Make alert count consistent with the alerts page u…
Browse files Browse the repository at this point in the history
…sing a filter (elastic#203003)

Closes elastic#202979 

## Summary

This PR fixes inconsistencies between the alerts shown on hosts view and
inventory and the alerts page. The fix includes using consistent rule
types and filters for both getting the alert count and filters applied
on the alert page

## How to Test

Service Alert ( Inventory )


https://github.com/user-attachments/assets/f3b626da-1a49-42dc-a989-48b13d15ae2c

Hosts view (+filters) Alerts (Inventory & Custom Threshold rule)



https://github.com/user-attachments/assets/2a490ad4-e2a4-43b5-b00f-d00ac27f9fd3

Single Host


https://github.com/user-attachments/assets/7c6a8cf7-f2a2-41f0-9f98-7e7543d4e7d5

cc: @roshan-elastic Ping to check the videos of the fix :)
  • Loading branch information
jennypavlova authored Dec 9, 2024
1 parent df0dfa5 commit 62c3aec
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export const AlertsSummaryContent = ({
)}
<EuiFlexItem grow={false}>
<LinkToAlertsPage
kuery={`"${assetId}"`}
kuery={`${assetIdField}:"${assetId}"`}
dateRange={dateRange}
data-test-subj="infraAssetDetailsAlertsTabAlertsShowAllButton"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import {
AlertConsumers,
INFRA_RULE_TYPE_IDS,
OBSERVABILITY_RULE_TYPE_IDS,
} from '@kbn/rule-data-utils';
import { AlertConsumers, OBSERVABILITY_RULE_TYPE_IDS } from '@kbn/rule-data-utils';
import { BrushEndListener, type XYBrushEvent } from '@elastic/charts';
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
import { useBoolean } from '@kbn/react-hooks';
Expand Down Expand Up @@ -47,11 +43,7 @@ export const AlertsTabContent = () => {

const { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable } =
triggersActionsUi;

const hostsWithAlertsKuery = hostNodes
.filter((host) => host.alertsCount)
.map((host) => `"${host.name}"`)
.join(' OR ');
const hostNamesKuery = hostNodes.map((host) => `host.name: "${host.name}"`).join(' OR ');

return (
<HeightRetainer>
Expand All @@ -73,7 +65,7 @@ export const AlertsTabContent = () => {
<LinkToAlertsPage
dateRange={searchCriteria.dateRange}
data-test-subj="infraHostAlertsTabAlertsShowAllButton"
kuery={`${hostsWithAlertsKuery}`}
kuery={hostNamesKuery}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down Expand Up @@ -146,7 +138,7 @@ const MemoAlertSummaryWidget = React.memo(
return (
<AlertSummaryWidget
chartProps={chartProps}
ruleTypeIds={INFRA_RULE_TYPE_IDS}
ruleTypeIds={OBSERVABILITY_RULE_TYPE_IDS}
consumers={INFRA_ALERT_CONSUMERS}
filter={alertsQuery}
fullSize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
import React from 'react';
import { EuiIcon, EuiLoadingSpinner, EuiBadge, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { OBSERVABILITY_RULE_TYPE_IDS } from '@kbn/rule-data-utils';
import { INFRA_ALERT_CONSUMERS } from '../../../../../../common/constants';
import { INFRA_RULE_TYPE_IDS } from '../../../../../../common/alerting/metrics/types';
import { useAlertsCount } from '../../../../../hooks/use_alerts_count';
import { useAlertsQuery } from '../../hooks/use_alerts_query';

export const AlertsTabBadge = () => {
const { alertsEsQuery } = useAlertsQuery();

const { alertsCount, loading, error } = useAlertsCount({
ruleTypeIds: INFRA_RULE_TYPE_IDS,
ruleTypeIds: OBSERVABILITY_RULE_TYPE_IDS,
consumers: INFRA_ALERT_CONSUMERS,
query: alertsEsQuery,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ const useKibanaMock = useKibana as jest.Mock;

const commonEntityFields: Partial<InventoryEntity> = {
entityLastSeenTimestamp: 'foo',
entityId: 'entity1',
entityId: '1',
};

describe('AlertsBadge', () => {
const mockAsKqlFilter = jest.fn();

beforeEach(() => {
jest.clearAllMocks();

Expand All @@ -29,6 +31,11 @@ describe('AlertsBadge', () => {
prepend: (path: string) => path,
},
},
entityManager: {
entityClient: {
asKqlFilter: mockAsKqlFilter,
},
},
},
});
});
Expand All @@ -52,10 +59,11 @@ describe('AlertsBadge', () => {
provider: null,
},
};
mockAsKqlFilter.mockReturnValue('host.name: "foo"');

render(<AlertsBadge entity={entity} />);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual(
`/app/observability/alerts?_a=(kuery:\"entity1\",status:active)`
`/app/observability/alerts?_a=(kuery:'host.name: "foo"',status:active)`
);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('1');
});
Expand All @@ -78,11 +86,40 @@ describe('AlertsBadge', () => {

alertsCount: 5,
};
mockAsKqlFilter.mockReturnValue('service.name: "bar"');

render(<AlertsBadge entity={entity} />);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual(
`/app/observability/alerts?_a=(kuery:\"entity1\",status:active)`
`/app/observability/alerts?_a=(kuery:'service.name: "bar"',status:active)`
);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('5');
});
it('render alerts badge for a service entity with multiple identity fields', () => {
const entity: InventoryEntity = {
...(commonEntityFields as InventoryEntity),
entityType: 'service',
entityDisplayName: 'foo',
entityIdentityFields: ['service.name', 'service.environment'],
entityDefinitionId: 'service',
service: {
name: 'bar',
environment: 'prod',
},
agent: {
name: 'node',
},
cloud: {
provider: null,
},
alertsCount: 2,
};

mockAsKqlFilter.mockReturnValue('service.name: "bar" AND service.environment: "prod"');

render(<AlertsBadge entity={entity} />);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.getAttribute('href')).toEqual(
`/app/observability/alerts?_a=(kuery:'service.name: "bar" AND service.environment: "prod"',status:active)`
);
expect(screen.queryByTestId('inventoryAlertsBadgeLink')?.textContent).toEqual('2');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ export function AlertsBadge({ entity }: { entity: InventoryEntity }) {
const {
services: {
http: { basePath },
entityManager,
},
} = useKibana();

const activeAlertsHref = basePath.prepend(
`/app/observability/alerts?_a=${rison.encode({
kuery: `"${entity.entityId}"`,
kuery: entityManager.entityClient.asKqlFilter({
entity: {
identity_fields: entity.entityIdentityFields,
},
...entity,
}),
status: 'active',
})}`
);
Expand Down

0 comments on commit 62c3aec

Please sign in to comment.