Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added pinned agent mechanic to inventory data, stats, and configuration for consistent functionality #7135

Merged
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
f7bac4a
feat: integrate ButtonExploreAgent component into MainModuleAgent for…
guidomodarelli Oct 29, 2024
e45f750
feat: add pinned agent mechanic across all pages for improved consist…
guidomodarelli Oct 29, 2024
91210a5
Merge branch '4.10.0' into enhancement/7130-all-pages-must-include-th…
guidomodarelli Oct 29, 2024
f221e07
feat: enhance ButtonPinnedAgent with clsx for dynamic class managemen…
guidomodarelli Oct 29, 2024
fd3f673
refactor: clean up agent selector styles by removing unnecessary focu…
guidomodarelli Oct 29, 2024
3ecbdbf
refactor: update WzButtonProps type to include EUI component properti…
guidomodarelli Oct 29, 2024
ead4fb8
refactor: improve ButtonPinnedAgent structure by adding data-test att…
guidomodarelli Oct 29, 2024
d72222b
test: add tests for explore agent button rendering in various agent t…
guidomodarelli Oct 29, 2024
dd54493
Merge branch '4.10.0' into enhancement/7130-all-pages-must-include-th…
asteriscos Oct 29, 2024
d10ff4e
test: add tests for explore agent button rendering in various agent t…
guidomodarelli Oct 29, 2024
6760160
refactor: add newline in ButtonPinnedAgent for improved readability a…
guidomodarelli Oct 29, 2024
41a1851
chore: update CHANGELOG to clarify pinned agent mechanic in inventory…
guidomodarelli Oct 30, 2024
21d5c03
refactor: add unPinAgent functionality to agent components for enhanc…
guidomodarelli Oct 30, 2024
d26e76f
refactor: remove unused EuiButtonEmpty import for cleaner code in con…
guidomodarelli Oct 30, 2024
e45733e
refactor: update useGenericRequest hook for improved type safety and …
guidomodarelli Oct 30, 2024
2695151
refactor: enhance type safety in InventoryMetrics and streamline API …
guidomodarelli Oct 30, 2024
89ac9ff
refactor: rename ErrorOrchestratorService to ErrorService for improve…
guidomodarelli Oct 30, 2024
8707d26
refactor: add JSDoc comments to GenericRequest for improved type safe…
guidomodarelli Oct 30, 2024
24a34b6
refactor: improve cluster handling logic and error management in WzCo…
guidomodarelli Oct 30, 2024
62b1c73
refactor: streamline AgentStats component with improved type safety a…
guidomodarelli Oct 30, 2024
81a2de7
Fix Prettier issue
guidomodarelli Oct 30, 2024
cafd694
refactor: update test descriptions in MainModuleAgent tests to clarif…
guidomodarelli Oct 31, 2024
fc7e020
refactor: remove deprecated Route for syscollector in AgentView to cl…
guidomodarelli Oct 31, 2024
aa62339
refactor: rename PartialRecordMock to DeepPartialRecordMock for bette…
guidomodarelli Oct 31, 2024
79c2718
feat: add mock agent data for Debian, Windows, and Darwin to enhance …
guidomodarelli Oct 31, 2024
25c6a66
refactor: replace inline AGENT mock with import from test mocks for i…
guidomodarelli Oct 31, 2024
050e7a1
test: add API call verification in AgentStats tests to ensure correct…
guidomodarelli Oct 31, 2024
daf5793
test: enhance AgentStats tests to verify API calls with correct agent…
guidomodarelli Oct 31, 2024
f925e75
test: refine AgentStats test description to clarify API call behavior…
guidomodarelli Oct 31, 2024
b5d65ab
test: improve AgentStats tests to ensure correct column structure, ti…
guidomodarelli Nov 1, 2024
28d05ff
refactor: update breadcrumb types in useGlobalBreadcrumb for improved…
guidomodarelli Nov 1, 2024
194cfa7
refactor: enhance withGlobalBreadcrumb HOC for better type handling a…
guidomodarelli Nov 1, 2024
57db511
refactor: replace hardcoded path in agent stats breadcrumb with SECTI…
guidomodarelli Nov 1, 2024
2827472
refactor: replace hardcoded agents-preview path with SECTIONS constan…
guidomodarelli Nov 1, 2024
79acf83
refactor: improve agent handling in withGlobalBreadcrumb for better m…
guidomodarelli Nov 1, 2024
fd6fd60
refactor: add global breadcrumb support in AgentView for better navig…
guidomodarelli Nov 1, 2024
60a8a91
refactor: update unPinAgent to navigate with new URL structure for im…
guidomodarelli Nov 1, 2024
cd17fd6
refactor: add withGlobalBreadcrumb HOC to enhance navigation structur…
guidomodarelli Nov 1, 2024
ffebd90
refactor: enhance AgentStats tests with clearer descriptions for colu…
guidomodarelli Nov 1, 2024
b071b9c
refactor: replace jQuery with native DOM method for setting title att…
guidomodarelli Nov 1, 2024
b993aea
refactor: improve syscollector metrics tests by adding agent ID handl…
guidomodarelli Nov 1, 2024
dfe7db8
refactor: add unit test for SoftwareTab rendering WindowsUpdatesTable…
guidomodarelli Nov 1, 2024
ead93b2
refactor: add unit test for WindowsUpdatesTable to verify correct hot…
guidomodarelli Nov 1, 2024
1b39e35
refactor: fix endpoint string in WindowsUpdatesTable test for accurat…
guidomodarelli Nov 1, 2024
30e08cc
refactor: enhance WindowsUpdatesTable test to validate API requests f…
guidomodarelli Nov 1, 2024
b633c52
refactor: handle potential undefined values in packages-table compone…
guidomodarelli Nov 1, 2024
6d51ac8
test: add test suite for PackagesTable to validate API requests and t…
guidomodarelli Nov 1, 2024
5ef2550
refactor: safeguard against undefined values for sorting fields in mu…
guidomodarelli Nov 1, 2024
b6bc5c8
test: add test suite for NetworkInterfacesTable to verify API request…
guidomodarelli Nov 1, 2024
5a05f76
test: update test descriptions in PackagesTable and WindowsUpdatesTab…
guidomodarelli Nov 1, 2024
9ef5ac0
test: add tests for NetworkPortsTable to verify correct API requests …
guidomodarelli Nov 1, 2024
adcd04d
test: rename test suite to NetworkPortsTable for clarity and proper c…
guidomodarelli Nov 1, 2024
03a9907
test: add NetworkSettingsTable tests to ensure correct rendering and …
guidomodarelli Nov 1, 2024
809de19
test: add tests for ProcessesTable to validate rendering and API requ…
guidomodarelli Nov 1, 2024
2f357df
fix: improve optional chaining to direct access for initial sorting f…
guidomodarelli Nov 4, 2024
924e8e6
test: introduce reusable functions for validating agent API requests …
guidomodarelli Nov 4, 2024
6523784
Merge branch '4.10.0' into enhancement/7130-all-pages-must-include-th…
guidomodarelli Nov 5, 2024
037c943
refactor(useGenericRequest): rename response state to data and update…
guidomodarelli Nov 5, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ All notable changes to the Wazuh app project will be documented in this file.
- Added ability to filter from File Integrity Monitoring registry inventory [#7119](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7119)
- Added new field columns and ability to select the visible fields in the File Integrity Monitoring Files and Registry tables [#7119](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7119)
- Added filter by value to document details fields [#7081](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7081)
- Added pinned agent mechanic to inventory data, stats, and configuration for consistent functionality [#7135](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7135)

### Changed

Expand Down
195 changes: 194 additions & 1 deletion plugins/main/public/components/agents/stats/agent-stats.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,60 @@
import React from 'react';
import { render, act } from '@testing-library/react';
import { render, act, RenderResult } from '@testing-library/react';
import { AgentStats } from './agent-stats';
import { queryDataTestAttr } from '../../../../test/public/query-attr';
import { CSS } from '../../../../test/utils/CSS';
import { WzRequest } from '../../../react-services';
import { AgentStatTable } from './table';

const agent000 = '000';
const agent001 = '001';

const apiReqMock = WzRequest.apiReq as jest.Mock;
const AgentStatTableMock = AgentStatTable as jest.Mock;

jest.mock('../../../react-services', () => ({
WzRequest: {
apiReq: jest.fn().mockResolvedValue(undefined),
},
}));

jest.mock('redux', () => ({
compose: () => (Component: React.JSX.Element) => Component,
__esModule: true,
}));

jest.mock('../../common/hocs', () => ({
withGlobalBreadcrumb: () => () => <></>,
withGuard: () => () => <></>,
withUserAuthorizationPrompt: () => () => <></>,
withErrorBoundary: () => () => <></>,
__esModule: true,
}));

jest.mock('../prompts', () => ({
PromptNoActiveAgentWithoutSelect: () => <></>,
PromptAgentFeatureVersion: () => <></>,
__esModule: true,
}));

jest.mock('../../../utils/applications', () => ({
endpointsSummary: {
id: 'endpoints-summary',
breadcrumbLabel: 'Endpoints',
},
}));

jest.mock('../../../react-services/navigation-service', () => ({
getInstance: () => ({
getUrlForApp: jest.fn().mockReturnValue('http://url'),
__esModule: true,
}),
}));

jest.mock('./table', () => ({
AgentStatTable: jest.fn(() => <></>),
}));

describe('AgentStats', () => {
it('should not render agent info ribbon', async () => {
await act(async () => {
Expand Down Expand Up @@ -65,4 +110,152 @@ describe('AgentStats', () => {
).toHaveLength(7);
});
});

it('should call api with correct agent ids and endpoints when changing agent', async () => {
apiReqMock.mockClear();

let rerender: RenderResult['rerender'];

await act(async () => {
({ rerender } = render(<AgentStats agent={{ id: agent000 }} />));
});

expect(apiReqMock).toHaveBeenCalledTimes(2);
expect(apiReqMock.mock.calls[0]).toEqual([
'GET',
`/agents/${agent000}/stats/logcollector`,
{},
]);
expect(apiReqMock.mock.calls[1]).toEqual([
'GET',
`/agents/${agent000}/stats/agent`,
{},
]);

apiReqMock.mockClear();

await act(async () => {
rerender(<AgentStats agent={{ id: agent001 }} />);
});

expect(apiReqMock).toHaveBeenCalledTimes(2);
expect(apiReqMock.mock.calls[0]).toEqual([
'GET',
`/agents/${agent001}/stats/logcollector`,
{},
]);
expect(apiReqMock.mock.calls[1]).toEqual([
'GET',
`/agents/${agent001}/stats/agent`,
{},
]);
});

it('should maintain column structure across multiple renders, either when changing agent or not', async () => {
AgentStatTableMock.mockClear();

const mockColumns = [
{
field: 'location',
name: 'Location',
sortable: true,
},
{
field: 'events',
name: 'Events',
sortable: true,
},
{
field: 'bytes',
name: 'Bytes',
sortable: true,
},
];

let rerender: RenderResult['rerender'];

await act(async () => {
({ rerender } = render(<AgentStats agent={{ id: agent000 }} />));
});

expect(AgentStatTableMock.mock.calls[0][0].columns).toEqual(mockColumns);
expect(AgentStatTableMock.mock.calls[1][0].columns).toEqual(mockColumns);

AgentStatTableMock.mockClear();

await act(async () => {
rerender(<AgentStats agent={{ id: agent001 }} />);
});

expect(AgentStatTableMock.mock.calls[0][0].columns).toEqual(mockColumns);
expect(AgentStatTableMock.mock.calls[1][0].columns).toEqual(mockColumns);
});

it('should apply correct titles after render and rerender, either when changing agent or not', async () => {
AgentStatTableMock.mockClear();

const mockDataStatLogcollectorTitle = 'Global';
const mockDataStatAgentTitle = 'Interval';

let rerender: RenderResult['rerender'];

await act(async () => {
({ rerender } = render(<AgentStats agent={{ id: agent000 }} />));
});

expect(AgentStatTableMock.mock.calls[0][0].title).toEqual(
mockDataStatLogcollectorTitle,
);
expect(AgentStatTableMock.mock.calls[1][0].title).toEqual(
mockDataStatAgentTitle,
);

AgentStatTableMock.mockClear();

await act(async () => {
rerender(<AgentStats agent={{ id: agent001 }} />);
});

expect(AgentStatTableMock.mock.calls[0][0].title).toEqual(
mockDataStatLogcollectorTitle,
);
expect(AgentStatTableMock.mock.calls[1][0].title).toEqual(
mockDataStatAgentTitle,
);
});

it('should update export csv filename correctly when changing agent', async () => {
AgentStatTableMock.mockClear();

const mockExportCSVFilename = (
agent000: string,
suffix: 'global' | 'interval',
) => `agent-stats-${agent000}-logcollector-${suffix}`;

let rerender: RenderResult['rerender'];

await act(async () => {
({ rerender } = render(<AgentStats agent={{ id: agent000 }} />));
});

expect(AgentStatTableMock.mock.calls[0][0].exportCSVFilename).toEqual(
mockExportCSVFilename(agent000, 'global'),
);
expect(AgentStatTableMock.mock.calls[1][0].exportCSVFilename).toEqual(
mockExportCSVFilename(agent000, 'interval'),
);

AgentStatTableMock.mockClear();

await act(async () => {
rerender(<AgentStats agent={{ id: agent001 }} />);
});

expect(AgentStatTableMock.mock.calls[0][0].exportCSVFilename).toEqual(
mockExportCSVFilename(agent001, 'global'),
);
expect(AgentStatTableMock.mock.calls[1][0].exportCSVFilename).toEqual(
mockExportCSVFilename(agent001, 'interval'),
);
});
});
93 changes: 49 additions & 44 deletions plugins/main/public/components/agents/stats/agent-stats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ import React, { useState, useEffect } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
EuiLoadingSpinner,
EuiPanel,
EuiPage,
EuiPageBody,
EuiSpacer,
EuiText,
} from '@elastic/eui';

import {
withGlobalBreadcrumb,
withGuard,
Expand Down Expand Up @@ -48,6 +44,8 @@ import { getErrorOrchestrator } from '../../../react-services/common-services';
import { endpointSummary } from '../../../utils/applications';
import NavigationService from '../../../react-services/navigation-service';
import WzRibbon from '../../common/ribbon/ribbon';
import { Agent } from '../../endpoints-summary/types';
import { SECTIONS } from '../../../sections';

const tableColumns = [
{
Expand All @@ -67,48 +65,51 @@ const tableColumns = [
},
];

const statsAgents: { title: string; field: string; render?: (value) => any }[] =
[
{
title: 'Status',
field: 'status',
},
{
title: 'Buffer',
field: 'buffer_enabled',
render: value => (value ? 'enabled' : 'disabled'),
},
{
title: 'Message buffer',
field: 'msg_buffer',
},
{
title: 'Messages count',
field: 'msg_count',
},
{
title: 'Messages sent',
field: 'msg_sent',
},
{
title: 'Last ack',
field: 'last_ack',
render: formatUIDate,
},
{
title: 'Last keep alive',
field: 'last_keepalive',
render: formatUIDate,
},
];
const statsAgents: {
title: string;
field: string;
render?: (value: any) => any;
}[] = [
{
title: 'Status',
field: 'status',
},
{
title: 'Buffer',
field: 'buffer_enabled',
render: value => (value ? 'enabled' : 'disabled'),
},
{
title: 'Message buffer',
field: 'msg_buffer',
},
{
title: 'Messages count',
field: 'msg_count',
},
{
title: 'Messages sent',
field: 'msg_sent',
},
{
title: 'Last ack',
field: 'last_ack',
render: formatUIDate,
},
{
title: 'Last keep alive',
field: 'last_keepalive',
render: formatUIDate,
},
];

export const MainAgentStats = compose(
withErrorBoundary,
withGlobalBreadcrumb(({ agent }) => [
{
text: endpointSummary.breadcrumbLabel,
href: NavigationService.getInstance().getUrlForApp(endpointSummary.id, {
path: `#/agents-preview`,
path: `#/${SECTIONS.AGENTS_PREVIEW}`,
}),
},
{ agent },
Expand Down Expand Up @@ -143,9 +144,13 @@ export const MainAgentStats = compose(
),
)(AgentStats);

export function AgentStats(props) {
interface AgentStatsProps {
agent: Agent;
}

export function AgentStats(props: AgentStatsProps) {
const { agent } = props;
const [loading, setLoading] = useState();
const [loading, setLoading] = useState(false);
const [dataStatLogcollector, setDataStatLogcollector] = useState({});
const [dataStatAgent, setDataStatAgent] = useState();
useEffect(() => {
Expand Down Expand Up @@ -175,16 +180,16 @@ export function AgentStats(props) {
severity: UI_ERROR_SEVERITIES.BUSINESS as UIErrorSeverity,
error: {
error: error,
message: error.message || error,
title: error.name || error,
message: (error as Error).message || (error as string),
title: (error as Error).name || (error as string),
},
};
getErrorOrchestrator().handleError(options);
} finally {
setLoading(false);
}
})();
}, []);
}, [agent.id]);
return (
<EuiPage>
<EuiPageBody>
Expand Down
Loading
Loading