Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

ACLP-Alerting: Filtering entities for firewall system alerts, add tooltip text to Entity Type component ([#13110](https://github.com/linode/manager/pull/13110))
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';

import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';

import { entityTypeTooltipText } from '../../constants';
import { EntityTypeSelect } from './EntityTypeSelect';

describe('EntityTypeSelect component tests', () => {
Expand Down Expand Up @@ -129,4 +130,25 @@ describe('EntityTypeSelect component tests', () => {
within(entityTypeDropdown).queryByRole('button', { name: 'Clear' })
).not.toBeInTheDocument();
});

it('should display tooltip text on hover of the help icon', async () => {
renderWithThemeAndHookFormContext({
component: (
<EntityTypeSelect
name="entity_type"
onEntityTypeChange={onEntityChange}
/>
),
});

const entityTypeContainer = screen.getByTestId(ENTITY_TYPE_SELECT_TEST_ID);
const helpButton =
within(entityTypeContainer).getByTestId('tooltip-info-icon');

await userEvent.hover(helpButton);

expect(await screen.findByText(entityTypeTooltipText)).toBeVisible();

await userEvent.unhover(helpButton);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import * as React from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import type { ControllerRenderProps, FieldPathByValue } from 'react-hook-form';

import type { Item } from '../../constants';
import { entityTypeTooltipText, type Item } from '../../constants';

import type { CreateAlertDefinitionForm } from '../types';

interface EntityTypeSelectProps {
Expand Down Expand Up @@ -55,6 +56,10 @@ export const EntityTypeSelect = (props: EntityTypeSelectProps) => {
options={entityTypeOptions}
placeholder="Select an Entity Type"
sx={{ marginTop: '5px' }}
textFieldProps={{
labelTooltipText: entityTypeTooltipText,
tooltipPosition: 'right',
}}
value={
entityTypeOptions.find((option) => option.value === field.value) ??
undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { waitFor } from '@testing-library/react';
import { waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';

import { alertFactory, notificationChannelFactory } from 'src/factories';
import {
alertFactory,
firewallMetricRulesFactory,
firewallNodebalancerMetricCriteria,
notificationChannelFactory,
} from 'src/factories';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { UPDATE_ALERT_SUCCESS_MESSAGE } from '../constants';
Expand Down Expand Up @@ -47,6 +52,9 @@
service_type: 'linode',
scope: 'entity',
});

const ENTITY_TYPE_SELECT_TEST_ID = 'entity-type-select';

describe('EditAlertDefinition component', () => {
it('renders the components of the form', { timeout: 20000 }, async () => {
const { findByPlaceholderText, getByLabelText, getByText } =
Expand Down Expand Up @@ -96,13 +104,69 @@

await waitFor(() => expect(mutateAsyncSpy).toHaveBeenCalledTimes(1));

expect(navigate).toHaveBeenLastCalledWith({
to: '/alerts/definitions',
expect(navigate).toHaveBeenLastCalledWith({

Check warning on line 107 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":107,"column":5,"nodeType":null,"messageId":"delete","endLine":107,"endColumn":7,"fix":{"range":[3894,3896],"text":""}}
to: '/alerts/definitions',

Check warning on line 108 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":108,"column":7,"nodeType":null,"messageId":"delete","endLine":108,"endColumn":9,"fix":{"range":[3946,3948],"text":""}}
});

Check warning on line 109 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Replace `······` with `····` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Replace `······` with `····`","line":109,"column":1,"nodeType":null,"messageId":"replace","endLine":109,"endColumn":7,"fix":{"range":[3975,3981],"text":" "}}
await waitFor(() => {

Check warning on line 110 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":110,"column":1,"nodeType":null,"messageId":"delete","endLine":110,"endColumn":3,"fix":{"range":[3985,3987],"text":""}}
expect(

Check warning on line 111 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Replace `········` with `······` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Replace `········` with `······`","line":111,"column":1,"nodeType":null,"messageId":"replace","endLine":111,"endColumn":9,"fix":{"range":[4013,4021],"text":" "}}
getByText(UPDATE_ALERT_SUCCESS_MESSAGE) // validate whether snackbar is displayed properly

Check warning on line 112 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Don't wrap `getBy*` query with `expect` & presence matchers like `toBeInTheDocument` or `not.toBeNull` as `getBy*` queries fail implicitly when element is not found Raw Output: {"ruleId":"testing-library/prefer-implicit-assert","severity":1,"message":"Don't wrap `getBy*` query with `expect` & presence matchers like `toBeInTheDocument` or `not.toBeNull` as `getBy*` queries fail implicitly when element is not found","line":112,"column":11,"nodeType":"Identifier","messageId":"preferImplicitAssert","endLine":112,"endColumn":20}

Check warning on line 112 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":112,"column":1,"nodeType":null,"messageId":"delete","endLine":112,"endColumn":3,"fix":{"range":[4029,4031],"text":""}}
).toBeInTheDocument();

Check warning on line 113 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":113,"column":7,"nodeType":null,"messageId":"delete","endLine":113,"endColumn":9,"fix":{"range":[4136,4138],"text":""}}
});

Check warning on line 114 in packages/manager/src/features/CloudPulse/Alerts/EditAlert/EditAlertDefinition.test.tsx

View workflow job for this annotation

GitHub Actions / ESLint Review (manager)

[eslint] reported by reviewdog 🐢 Delete `··` Raw Output: {"ruleId":"prettier/prettier","severity":1,"message":"Delete `··`","line":114,"column":5,"nodeType":null,"messageId":"delete","endLine":114,"endColumn":7,"fix":{"range":[4165,4167],"text":""}}
});
await waitFor(() => {
expect(
getByText(UPDATE_ALERT_SUCCESS_MESSAGE) // validate whether snackbar is displayed properly
).toBeInTheDocument();

it('should render EntityTypeSelect for firewall with Linode entity type', () => {
const linodeFirewallAlertDetails = alertFactory.build({
id: 1,
rule_criteria: {
rules: [firewallMetricRulesFactory.build()],
},
scope: 'entity',
service_type: 'firewall',
});

const { getByTestId } = renderWithTheme(
<EditAlertDefinition
alertDetails={linodeFirewallAlertDetails}
serviceType="firewall"
/>
);

const entityTypeSelect = getByTestId(ENTITY_TYPE_SELECT_TEST_ID);
expect(entityTypeSelect).toBeVisible();

const combobox = within(entityTypeSelect).getByRole('combobox');
expect(combobox).toHaveAttribute('value', 'Linodes');
});

it('should render EntityTypeSelect for firewall with NodeBalancer entity type', () => {
const nodebalancerFirewallAlertDetails = alertFactory.build({
id: 2,
rule_criteria: {
rules: [firewallNodebalancerMetricCriteria.build()],
},
scope: 'entity',
service_type: 'firewall',
});

const { getByTestId } = renderWithTheme(
<EditAlertDefinition
alertDetails={nodebalancerFirewallAlertDetails}
serviceType="firewall"
/>
);

const entityTypeSelect = getByTestId(ENTITY_TYPE_SELECT_TEST_ID);
expect(entityTypeSelect).toBeVisible();

const combobox = within(entityTypeSelect).getByRole('combobox');
expect(combobox).toHaveAttribute('value', 'NodeBalancers');
});

it('should not render EntityTypeSelect for non-firewall service types', () => {
const { queryByTestId } = renderWithTheme(
<EditAlertDefinition alertDetails={alertDetails} serviceType="linode" />
);

expect(queryByTestId(ENTITY_TYPE_SELECT_TEST_ID)).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Breadcrumb } from 'src/components/Breadcrumb/Breadcrumb';
import { useEditAlertDefinition } from 'src/queries/cloudpulse/alerts';

import { AlertResources } from '../AlertsResources/AlertsResources';
import { entityLabelMap } from '../constants';
import { isResourcesEqual } from '../Utils/AlertResourceUtils';
import { getAlertBoxStyles } from '../Utils/utils';
import { EditAlertResourcesConfirmDialog } from './EditAlertResourcesConfirmationDialog';
Expand Down Expand Up @@ -85,6 +86,14 @@ export const EditAlertResources = (props: EditAlertProps) => {
type,
} = alertDetails;

const entityType =
serviceType === 'firewall'
? alertDetails.rule_criteria.rules[0]?.label.includes(
entityLabelMap['nodebalancer']
)
? 'nodebalancer'
: 'linode'
: undefined;
return (
<>
<Breadcrumb crumbOverrides={overrides} pathname={newPathname} />
Expand All @@ -100,6 +109,7 @@ export const EditAlertResources = (props: EditAlertProps) => {
alertLabel={label}
alertResourceIds={entity_ids}
alertType={type}
entityType={entityType}
handleResourcesSelection={handleResourcesSelection}
isSelectionsNeeded
serviceType={service_type}
Expand Down
3 changes: 3 additions & 0 deletions packages/manager/src/features/CloudPulse/Alerts/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,6 @@ export const entityLabelMap = {
linode: 'Linode',
nodebalancer: 'Node Balancer',
};

export const entityTypeTooltipText =
'Select a firewall entity type to filter the list in the Entities section. The metrics and dimensions in the Criteria section will update automatically based on your selection.';
54 changes: 54 additions & 0 deletions packages/manager/src/mocks/serverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3273,6 +3273,28 @@ export const handlers = [
rules: [firewallNodebalancerMetricCriteria.build()],
},
}),
alertFactory.build({
id: 340,
label: 'Firewall-nodebalancer-system',
type: 'system',
service_type: 'firewall',
entity_ids: ['25'],
rule_criteria: {
rules: [
firewallNodebalancerMetricCriteria.build({ dimension_filters: [] }),
],
},
}),
alertFactory.build({
id: 123,
label: 'Firewall-linode-system',
type: 'system',
service_type: 'firewall',
entity_ids: ['1', '4'],
rule_criteria: {
rules: [firewallMetricRulesFactory.build()],
},
}),
...alertFactory.buildList(3, { status: 'enabling', type: 'user' }),
...alertFactory.buildList(3, { status: 'disabling', type: 'user' }),
...alertFactory.buildList(3, { status: 'provisioning', type: 'user' }),
Expand Down Expand Up @@ -3344,6 +3366,38 @@ export const handlers = [
})
);
}
if (params.id === '340' && params.serviceType === 'firewall') {
return HttpResponse.json(
alertFactory.build({
id: 340,
label: 'Firewall - nodebalancer - system',
type: 'system',
service_type: 'firewall',
entity_ids: ['25'],
rule_criteria: {
rules: [
firewallNodebalancerMetricCriteria.build({
dimension_filters: [],
}),
],
},
})
);
}
if (params.id === '123' && params.serviceType === 'firewall') {
return HttpResponse.json(
alertFactory.build({
id: 123,
label: 'Firewall-linode-system',
type: 'system',
service_type: 'firewall',
entity_ids: ['1', '4'],
rule_criteria: {
rules: [firewallMetricRulesFactory.build()],
},
})
);
}
if (params.id !== undefined) {
return HttpResponse.json(
alertFactory.build({
Expand Down