diff --git a/CHANGELOG.md b/CHANGELOG.md index b626ef9d35..05f54d57c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Changed the warning icon in events view to a info icon [#7057](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7057) - Changed feature container margins to ensure consistent separation and uniform design. [#7034](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7034) - Changed the initial width to the default columns on each selected field [#7059](https://github.com/wazuh/wazuh-dashboard-plugins/issues/7059) +- Changed inventory, stats and configuration page to use tabs [#7089](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7089) ### Fixed diff --git a/docker/imposter/wazuh-config.yml b/docker/imposter/wazuh-config.yml index 8f99c6897e..cc745fa33e 100755 --- a/docker/imposter/wazuh-config.yml +++ b/docker/imposter/wazuh-config.yml @@ -1,6 +1,6 @@ --- plugin: openapi -specFile: https://raw.githubusercontent.com/wazuh/wazuh/master/api/api/spec/spec.yaml +specFile: https://raw.githubusercontent.com/wazuh/wazuh/4.10.0/api/api/spec/spec.yaml system: stores: # this store is preloaded from file diff --git a/plugins/main/common/services/wz_agent_status.ts b/plugins/main/common/services/wz_agent_status.ts index 3c37e98a92..694e536917 100644 --- a/plugins/main/common/services/wz_agent_status.ts +++ b/plugins/main/common/services/wz_agent_status.ts @@ -1,14 +1,27 @@ -import { UI_COLOR_AGENT_STATUS, UI_LABEL_NAME_AGENT_STATUS, API_NAME_AGENT_STATUS } from '../constants'; +import { + UI_COLOR_AGENT_STATUS, + UI_LABEL_NAME_AGENT_STATUS, + API_NAME_AGENT_STATUS, +} from '../constants'; -type TAgentStatus = typeof API_NAME_AGENT_STATUS[keyof typeof API_NAME_AGENT_STATUS]; +export type TAgentStatus = + (typeof API_NAME_AGENT_STATUS)[keyof typeof API_NAME_AGENT_STATUS]; -type TAgentStatusColor = typeof UI_COLOR_AGENT_STATUS[keyof typeof UI_COLOR_AGENT_STATUS]; -type TAgentStatusLabel = typeof UI_LABEL_NAME_AGENT_STATUS[keyof typeof UI_LABEL_NAME_AGENT_STATUS]; +type TAgentStatusColor = + (typeof UI_COLOR_AGENT_STATUS)[keyof typeof UI_COLOR_AGENT_STATUS]; +type TAgentStatusLabel = + (typeof UI_LABEL_NAME_AGENT_STATUS)[keyof typeof UI_LABEL_NAME_AGENT_STATUS]; -export function agentStatusColorByAgentStatus(status: TAgentStatus): TAgentStatusColor{ - return UI_COLOR_AGENT_STATUS[status] || UI_COLOR_AGENT_STATUS.default; +export function agentStatusColorByAgentStatus( + status: TAgentStatus, +): TAgentStatusColor { + return UI_COLOR_AGENT_STATUS[status] || UI_COLOR_AGENT_STATUS.default; } -export function agentStatusLabelByAgentStatus(status: TAgentStatus): TAgentStatusLabel{ - return UI_LABEL_NAME_AGENT_STATUS[status] || UI_LABEL_NAME_AGENT_STATUS.default; -} \ No newline at end of file +export function agentStatusLabelByAgentStatus( + status: TAgentStatus, +): TAgentStatusLabel { + return ( + UI_LABEL_NAME_AGENT_STATUS[status] || UI_LABEL_NAME_AGENT_STATUS.default + ); +} diff --git a/plugins/main/public/app-router.tsx b/plugins/main/public/app-router.tsx index 842c07ae03..8f45a6b3cd 100644 --- a/plugins/main/public/app-router.tsx +++ b/plugins/main/public/app-router.tsx @@ -21,6 +21,7 @@ import { Settings } from './components/settings'; import { WzSecurity } from './components/security'; import $ from 'jquery'; import NavigationService from './react-services/navigation-service'; +import { SECTIONS } from './sections'; export function Application(props) { const dispatch = useDispatch(); @@ -66,33 +67,41 @@ export function Application(props) { - + - + - + } > - - + + } > } > diff --git a/plugins/main/public/components/agents/agent-status.tsx b/plugins/main/public/components/agents/agent-status.tsx index a7967a0d92..65179ba825 100644 --- a/plugins/main/public/components/agents/agent-status.tsx +++ b/plugins/main/public/components/agents/agent-status.tsx @@ -2,15 +2,29 @@ import React from 'react'; import { agentStatusColorByAgentStatus, agentStatusLabelByAgentStatus, + TAgentStatus, } from '../../../common/services/wz_agent_status'; import { ColumnWithStatusIcon } from './column-with-status-icon'; import { EuiIconTip } from '@elastic/eui'; import { AGENT_STATUS_CODE } from '../../../common/constants'; import '../../styles/common.scss'; +import { Agent } from '../endpoints-summary/types'; -export const AgentStatus = ({ status, children = null, style = {}, agent }) => { +interface AgentStatusProps { + status: TAgentStatus; + children?: string | null; + style?: React.CSSProperties; + agent?: Agent; +} + +export const AgentStatus = ({ + status, + children, + style = {}, + agent, +}: AgentStatusProps) => { const statusCodeAgent = AGENT_STATUS_CODE.find( - (status: StatusCodeAgent) => status.STATUS_CODE === agent?.status_code, + status => status.STATUS_CODE === agent?.status_code, ); return (
({ + WzRequest: { + apiReq: jest.fn().mockResolvedValue(undefined), + }, +})); + +describe('AgentStats', () => { + it('should not render agent info ribbon', async () => { + await act(async () => { + const { container } = render(); + + const agentInfoRibbon = container.querySelector( + queryDataTestAttr('agent-info'), + ); + expect(agentInfoRibbon).toBeFalsy(); + }); + }); + + it('should render stats info ribbon', async () => { + await act(async () => { + const { container } = render(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-status')), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr('ribbon-item-buffer_enabled'), + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-msg_buffer')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-msg_count')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-msg_sent')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-last_ack')), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr('ribbon-item-last_keepalive'), + ), + ).toBeTruthy(); + + expect( + container.querySelectorAll( + queryDataTestAttr('ribbon-item-', CSS.Attribute.Substring), + ), + ).toHaveLength(7); + }); + }); +}); diff --git a/plugins/main/public/components/agents/stats/agent-stats.tsx b/plugins/main/public/components/agents/stats/agent-stats.tsx index 2d57187359..98f966281c 100644 --- a/plugins/main/public/components/agents/stats/agent-stats.tsx +++ b/plugins/main/public/components/agents/stats/agent-stats.tsx @@ -47,6 +47,7 @@ import { 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'; const tableColumns = [ { @@ -142,7 +143,8 @@ export const MainAgentStats = compose( ), )(AgentStats); -function AgentStats({ agent }) { +export function AgentStats(props) { + const { agent } = props; const [loading, setLoading] = useState(); const [dataStatLogcollector, setDataStatLogcollector] = useState({}); const [dataStatAgent, setDataStatAgent] = useState(); @@ -186,33 +188,20 @@ function AgentStats({ agent }) { return ( - - - - - {statsAgents.map(stat => ( - - - {stat.title}:{' '} - {loading ? ( - - ) : ( - - {dataStatAgent !== undefined - ? stat.render - ? stat.render(dataStatAgent[stat.field]) - : dataStatAgent?.[stat.field] - : '-'} - - )} - - - ))} - - - - - + ({ + key: stat.field, + label: stat.title, + isLoading: loading, + value: + dataStatAgent !== undefined + ? stat.render + ? stat.render(dataStatAgent[stat.field]) + : dataStatAgent?.[stat.field] + : '-', + }))} + /> +
+ +
+
+ + + Cores [object Object] +
+
+
+ +
+
+ + + Memory [object Object] +
+
+
+ +
+
+ + + Arch [object Object] +
+
+
+ +
+
+ + + Operating system [object Object] +
+
+
+ +
+
+ + + CPU [object Object] +
+ +
+ +
+
+ + + Host name [object Object] +
+ +
+ +
+
+ + + Board serial [object Object] +
+ +
+ +
+
+ + + Last scan [object Object] +
- -
+
-

- Network interfaces - - (0) - -

-
-
-
-
-
-
- + Network interfaces + + (0) + + +
+
- + + + + Refresh + + + +
+
+ +
+
-
-
-
-
-
+
-
+
-
- -
-
+
-
+
+
- - WQL - - - + + + WQL + + + +
+
-
-
-
-
-
+
-
-
+
+
+
+
+ +
+
+
+
+
+
+ + + + + - - - - - - + + + + +
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
-
- - -
+
+ + - - - - - - - - - - - + - + + + + + + + + +
-
- - - Name - - - - - - + - - MAC - - - - - + - - State - + + + Type + + + +
+
+ + No items found + +
+
+ + + + + + +
+
+
+
+
+
+
+
+

+ Network ports + + (0) - -

+ + + +
+
+
-
+
-
+
- - No items found - +
+
+ +
+
+
-
+
+
+ + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + +
+
+ + No items found + +
+
+
+
-
-
-

- Network ports - - (0) - -

+

+ Network settings + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -686,183 +1209,238 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + - + - + - + - - - - - + + + + -
- - No items found - -
- - - -
-
- +
+
- - Local port - - - - + + Interface + + + + + - - Local IP address - - - - + - - State + + Netmask + - - - - - - Protocol + + Protocol + - - -
+ + + +
+ + No items found + +
+ + + + +
@@ -870,23 +1448,301 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
+
+`; + +exports[`Inventory data Network Network interfaces table A Linux agent should render network interfaces table with correct columns and title. 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
-

- Network settings - - (0) - -

-
-
-
-
-
-
- + Network interfaces + + (0) + + +
+
- -
-
-
-
-
-
-
-
-
-
-
+
-
- -
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - -
-
- - - - - +
-
- -
-
- - No items found - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

- Packages - - (0) - -

-
-
-
-
-
-
- -
-
- +
+
-
-
-
-
-
+
-
+
-
- -
-
+
-
+
+
- - WQL - - - + + + WQL + + + +
+
-
-
-
-
-
+
-
-
+
+
+
+
+ +
+
+
+
+
+
+ + + + + - - + +
+
+ + - - - - - - - - - - + + + + + + + + + +
-
- - - Name - - + + State + + + + + + + +
+
+ + No items found + +
+
+ + + + + + +
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ -
+
-
+ + + + +
+
+
+
+
- +
+
+
+
+
+ + + +
+
+
+
+
+
+
- +
+
+ +
+
+
+
+
+
+ + + + - - - - - - - + + + + + + - - No items found - - - - - -
+
- Format + + Local port + - - - - - Location + + Local IP address + - - - - - Description + + Process + - - -
-
+
+ + + PID + + + + + + +
+
+ + No items found + +
+ +
+
+
-
-
-
-
-

- Processes - - (0) - -

+

+ Network settings + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -1852,262 +2692,238 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + - + - + - + - + - + + + + - - - - - - - - - -
-
-
+
- - Name + + Interface + + - - - - - - - Effective user + + Address + - - - - - - PID + + Netmask + - - - - - - Parent PID + + Protocol + - - - - - - VM size + + Broadcast + - - - +
-
- - No items found - -
-
+
+ + + + +
@@ -2118,139 +2934,298 @@ exports[`Inventory component A Apple agent should be well rendered. 1`] = `
`; -exports[`Inventory component A Linux agent should be well rendered. 1`] = ` +exports[`Inventory data Network Network interfaces table A Windows agent should render network interfaces table with correct columns and title. 1`] = `
+ +
+
+ + + Cores [object Object] +
+
+
+ +
+
+ + + Memory [object Object] +
+
+
+ +
+
+ + + Arch [object Object] +
+
+
+ +
+
+ + + Operating system [object Object] +
+
+
+ +
+
+ + + CPU [object Object] +
+
+
+ +
+
+ + + Host name [object Object] +
+
+
+ +
+
+ + + Board serial [object Object] +
+
+
+ +
+
+ + + Last scan [object Object] +
-
-
+
-

- Network interfaces - - (0) - -

-
-
-
-
-
-
- + Network interfaces + + (0) + + +
+
- -
-
-
-
-
-
-
-
-
-
-
+
-
- -
-
-
- +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + WQL + + + +
+
-
-
-
-
-
+
-
-
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + - - - - + + + + + + +
+
+ + No items found + +
+
- + + + +
+
+
+
+
-
- - - - - - - - - - + +
+
+ +
+
+ + + + + + +
+
+
+
-
- - -
-
+
+
+

+ Network ports + + (0) + +

+
+
+ +
+
+
-
- - +
-
- - + + + + +
+
+
+
+
- -
+
- - No items found - -
-
-
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + + + +
+
+ + No items found + +
+
+
+
+
-
-
-

- Network ports - - (0) - -

+

+ Network settings + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -2803,214 +4167,238 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + - + - + - + - + - + + + + - - - - - - - - - -
-
- +
+
- - Local port - - - - + + Interface + + + + + - - Local IP address - - - - + + Address + + + + - - Process - - - - + + Netmask + + + + - - PID - - - - + - - State + + Broadcast + - - - +
-
- - No items found - -
-
+
+ + + + +
@@ -3018,572 +4406,10845 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
+
+`; + +exports[`Inventory data Network Network ports table A Apple agent should render network ports table with correct columns and title. 1`] = ` +
-
-
-
-

- Network settings - - (0) - -

-
-
-
- +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

- Refresh - - - + Network interfaces + + (0) + +

+
+
- + + + + Refresh + + + +
+
+ +
+
-
-
-
-
-
+
-
+
-
- +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
- - + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Network Network ports table A Linux agent should render network ports table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network interfaces + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + Process + + + + + + PID + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Network Network ports table A Windows agent should render network ports table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network interfaces + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Network Network settings table A Apple agent should render network settings table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network interfaces + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Network Network settings table A Linux agent should render network settings table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network interfaces + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + Process + + + + + + PID + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Network Network settings table A Windows agent should render network settings table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network interfaces + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network ports + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + Local port + + + + + + Local IP address + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Network settings + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Processes A Apple agent should render processes table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Processes + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Processes A Linux agent should render processes table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ Processes + + (0) + +

+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + No items found + +
+
+
+
+
+
+
+
+`; + +exports[`Inventory data Processes A Windows agent should render processes table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ class="euiText euiText--small euiStat__description" + > + +
+
+
+
+
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - - - - - -
-
- - - - - - - - - -
-
- - No items found - -
-
-
+
+
-
-
+
-

- Packages - - (0) - -

+

+ Processes + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -3591,239 +15252,288 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + + + + - + - + - + - + + + + - - - - - - - - - -
-
-
+
+ + + + + + - - Name + + VM size + - - - - - - - Architecture + + Priority + - - - - - - Version + + NLWP + - - - - - - Vendor + + Command + - - - +
-
- - No items found - -
-
+
+ + + + +
@@ -3831,167 +15541,450 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
+
+`; + +exports[`Inventory data Software Packages table A Apple agent should render software table with correct columns and title. 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
-

- Processes - - (0) - -

+

+ Packages + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
+ +
+
- + +
@@ -3999,408 +15992,240 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - - - - - - - + + - + - + - + - + - + + + + - - - - - - - - - -
-
- - - - - - - - - - - - -
+
- - Argvs + + Name + + - - - - - - VM size + + Version + - - - - - - Size + + Format + - - - - - - Session + + Location + - - - - - - Priority + + Description + - - - +
-
- - No items found - -
-
+
+ + + + +
@@ -4411,283 +16236,447 @@ exports[`Inventory component A Linux agent should be well rendered. 1`] = `
`; -exports[`Inventory component A Windows agent should be well rendered. 1`] = ` +exports[`Inventory data Software Packages table A Linux agent should render software table with correct columns and title. 1`] = `
+ +
+
+ + + Cores [object Object] +
+
+
+ +
+
+ + + Memory [object Object] +
+
+
+ +
+
+ + + Arch [object Object] +
+
+
+ +
+
+ + + Operating system [object Object] +
+
+
+ +
+
+ + + CPU [object Object] +
+
+
+ +
+
+ + + Host name [object Object] +
+
+
+ +
+
+ + + Board serial [object Object] +
+
+
+ +
+
+ + + Last scan [object Object] +
-
-
+
-

- Network interfaces - - (0) - -

+

+ Packages + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -4695,401 +16684,692 @@ exports[`Inventory component A Windows agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + - + - + - + - + - - - - - + + + -
- - No items found - -
- - - -
-
-
+
- - Name + + Name + + - - - - - - - MAC + + Architecture + - - - - - - State + + Version + - - - - - - MTU + + Vendor + - - - - - - Type + + Description + - - -
+ +
+ + No items found + +
+ + + + +
+
+
+`; + +exports[`Inventory data Software Packages table A Windows agent should render software table with correct columns and title. 1`] = ` +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
-

- Network ports - - (0) - -

+

+ Windows updates + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -5097,375 +17377,308 @@ exports[`Inventory component A Windows agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - - - - + + - + + + + - - - - - - - - - -
-
- - - Local port - - - - - - Local IP address - - - - - -
+
- - State + + Update code + + - - - +
-
- - No items found - -
-
+
+ + + + +
-
-
-
-
-

- Network settings - - (0) - -

+

+ Packages + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -5473,1095 +17686,976 @@ exports[`Inventory component A Windows agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - + + - + - + - + - + + + + - - - - - - - - - -
-
-
+
- - Interface + + Name + + - - - - - - - Address + + Architecture + - - - - - - Netmask + + Version + - - - - - - Protocol + + Vendor + - - - +
-
- - No items found - -
-
+
+ + + + +
+
+
+`; + +exports[`Inventory data Software Windows updates table A Windows agent should render software table with correct columns and title. 1`] = ` +
+
- -
+
+
+
+
+
+
+
+ Memory + +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
- -
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
- - - - - - - - - - - -
-
- -
-
- - No items found - -
-
-
+
+
-
-
+
-

- Packages - - (0) - -

+

+ Windows updates + + (0) + +

+
-
-
- -
-
- -
-
-
-
-
-
-
-
-
-
-
-
+
+
+ -
-
-
+ + + + Export formatted + + +
-
-
-
+
-
+
+
- - - Sorting + + WQL + - - -
-
-
-
-
-
- - - - - - + +
-
- - + + + + + + + + +
+
+
+
+
+
+
- -
- + + + + + + + + + + - + + + + - - - - - - - - - -
+
- - Version + + Update code + + - - - +
-
- - No items found - -
-
+ + +
+
-
-
-
-
-

- Processes - - (0) - -

+

+ Packages + + (0) + +

+
-
-
- -
-
- +
+
+ + +
-
-
-
-
+
+
-
-
-
- -
+
+
-
+
+
- - WQL + + WQL + - - + +
@@ -6569,287 +18663,216 @@ exports[`Inventory component A Windows agent should be well rendered. 1`] = `
-
-
-
-
-
+
+
+
-
+
- + +
-
- - - - - - + + - + - + - + - + + + + - - - - - - - - - -
-
- - - - -
+
- - Parent PID + + Name + + - - - - - - VM size + + Architecture + - - - - - - Priority + + Version + - - - - - - NLWP + + Vendor + - - - +
-
- - No items found - -
-
+
+ + + + +
diff --git a/plugins/main/public/components/agents/syscollector/components/network-interfaces-table.tsx b/plugins/main/public/components/agents/syscollector/components/network-interfaces-table.tsx index 862c4e2cbe..2fde2feae3 100644 --- a/plugins/main/public/components/agents/syscollector/components/network-interfaces-table.tsx +++ b/plugins/main/public/components/agents/syscollector/components/network-interfaces-table.tsx @@ -10,7 +10,7 @@ const sortFieldSuggestion = (a, b) => (a.label > b.label ? 1 : -1); export const NetworkInterfacesTable = ({ agent }) => { return ( - + (a.label > b.label ? 1 : -1); export const NetworkPortsTable = withSOPlatformGuard( ({ agent, soPlatform }) => { return ( - + (a.label > b.label ? 1 : -1); export const NetworkSettingsTable = ({ agent }) => { return ( - + (a.label > b.label ? 1 : -1); export const PackagesTable = withSOPlatformGuard(({ agent, soPlatform }) => { return ( - + (a.label > b.label ? 1 : -1); export const ProcessesTable = withSOPlatformGuard(({ agent, soPlatform }) => { return ( - + ({ + formatUIDate: jest.fn().mockReturnValue('2022-06-27T16:09:49+00:00'), +})); + +jest.mock('../../../common/hooks/useGenericRequest', () => ({ + useGenericRequest: jest.fn().mockReturnValue({ + isLoading: false, + data: { + hardware: { + cpu: { + name: 'Intel(R) Core(TM) i7-10710U CPU @ 1.10GHz', + cores: 4, + threads: 8, + }, + ram: { + total: '16.0 GB', + }, + }, + os: { + platform: 'windows', + version: '10.0.19045', + }, + }, + error: '', + }), +})); + +describe('Syscollector metrics', () => { + it('should render inventory metrics', () => { + const { container } = render(); + + const inventoryMetrics = container.querySelector( + queryDataTestAttr('syscollector-metrics'), + ); + + expect(inventoryMetrics).toBeTruthy(); + }); + + it('should render syscollector ribbon items', () => { + const { container } = render(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-cores')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-memory')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-arch')), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr('ribbon-item-operating-system'), + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-cpu')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-hostname')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-board-serial')), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr('ribbon-item-last-scan')), + ).toBeTruthy(); + + expect( + container.querySelectorAll( + queryDataTestAttr('ribbon-item-', CSS.Attribute.Substring), + ), + ).toHaveLength(8); + }); +}); diff --git a/plugins/main/public/components/agents/syscollector/components/syscollector-metrics.tsx b/plugins/main/public/components/agents/syscollector/components/syscollector-metrics.tsx index be0cf63f59..682e29585b 100644 --- a/plugins/main/public/components/agents/syscollector/components/syscollector-metrics.tsx +++ b/plugins/main/public/components/agents/syscollector/components/syscollector-metrics.tsx @@ -1,15 +1,13 @@ import React, { useState } from 'react'; -import { - EuiPanel, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiLoadingSpinner, - EuiIcon, -} from '@elastic/eui'; -import mapValues from 'lodash'; +import { EuiPanel, EuiIcon } from '@elastic/eui'; +import _ from 'lodash'; import { useGenericRequest } from '../../../common/hooks/useGenericRequest'; import { formatUIDate } from '../../../../react-services/time-service'; +import WzRibbon from '../../../common/ribbon/ribbon'; +import { + IRibbonItem, + RibbonItemLabel, +} from '../../../common/ribbon/ribbon-item'; export function InventoryMetrics({ agent }) { const [params, setParams] = useState({}); @@ -31,8 +29,7 @@ export function InventoryMetrics({ agent }) { if ( !syscollector.isLoading && - (mapValues.isEmpty(syscollector.data.hardware) || - mapValues.isEmpty(syscollector.data.os)) + (_.isEmpty(syscollector.data.hardware) || _.isEmpty(syscollector.data.os)) ) { return ( @@ -42,106 +39,72 @@ export function InventoryMetrics({ agent }) { ); } - return ( - - - - - Cores:{' '} - {syscollector.isLoading ? ( - - ) : syscollector.data.hardware.cpu?.cores ? ( - {syscollector.data.hardware.cpu.cores} - ) : ( - - - )} - - - - - Memory:{' '} - {syscollector.isLoading ? ( - - ) : syscollector.data.hardware.ram?.total ? ( - - {(syscollector.data.hardware.ram.total / 1024).toFixed(2)} MB - - ) : ( - - - )} - - - - - Arch:{' '} - {syscollector.isLoading ? ( - - ) : ( - {syscollector.data.os.architecture} - )} - - - - - Operating system:{' '} - {syscollector.isLoading ? ( - - ) : ( - - {syscollector.data.os.os.name} {syscollector.data.os.os.version} - - )} - - - - - CPU:{' '} - {syscollector.isLoading ? ( - - ) : syscollector.data.hardware.cpu?.name ? ( - {syscollector.data.hardware.cpu.name} - ) : ( - - - )} - - - - - Host name:{' '} - {syscollector.isLoading ? ( - - ) : syscollector.data.os.hostname ? ( - {syscollector.data.os.hostname} - ) : ( - - - )} - - - - - Board serial:{' '} - {syscollector.isLoading ? ( - - ) : syscollector.data.hardware.board_serial ? ( - {syscollector.data.hardware.board_serial} - ) : ( - - - )} - - - - - Last scan:{' '} - {syscollector.isLoading ? ( - - ) : ( - - {offsetTimestamp('', syscollector.data.os.scan.time)} - - )} - - - - - ); + const render = () => { + const items: IRibbonItem[] = [ + { + key: 'cores', + label: 'Cores', + value: syscollector?.data?.hardware?.cpu?.cores, + isLoading: syscollector.isLoading, + style: { maxWidth: 100 }, + }, + { + key: 'memory', + label: 'Memory', + value: syscollector?.data?.hardware?.ram?.total + ? `${(syscollector?.data?.hardware?.ram?.total / 1024).toFixed(2)} MB` + : '-', + isLoading: syscollector.isLoading, + style: { maxWidth: 100 }, + }, + { + key: 'arch', + label: 'Arch', + value: syscollector?.data?.os?.architecture, + isLoading: syscollector.isLoading, + style: { maxWidth: 100 }, + }, + { + key: RibbonItemLabel.OPERATING_SYSTEM, + value: syscollector?.data?.os, + label: 'Operating system', + isLoading: syscollector.isLoading, + style: { maxWidth: 200 }, + }, + { + key: 'cpu', + label: 'CPU', + value: syscollector?.data?.hardware?.cpu?.name, + isLoading: syscollector.isLoading, + style: { maxWidth: 250 }, + }, + { + key: 'hostname', + label: 'Host name', + value: syscollector?.data?.os?.hostname, + isLoading: syscollector.isLoading, + style: { maxWidth: 100 }, + }, + { + key: 'board-serial', + label: 'Board serial', + value: syscollector?.data?.hardware?.board_serial, + isLoading: syscollector.isLoading, + style: { maxWidth: 100 }, + }, + { + key: 'last-scan', + label: 'Last scan', + value: syscollector?.data?.os?.scan?.time + ? offsetTimestamp('', syscollector?.data?.os?.scan?.time) + : '-', + isLoading: syscollector.isLoading, + style: { maxWidth: 180 }, + }, + ]; + + return ; + }; + + return render(); } diff --git a/plugins/main/public/components/agents/syscollector/components/windows-updates-table.tsx b/plugins/main/public/components/agents/syscollector/components/windows-updates-table.tsx index 65e3937cca..34410bc467 100644 --- a/plugins/main/public/components/agents/syscollector/components/windows-updates-table.tsx +++ b/plugins/main/public/components/agents/syscollector/components/windows-updates-table.tsx @@ -10,7 +10,7 @@ const sortFieldSuggestion = (a, b) => (a.label > b.label ? 1 : -1); export const WindowsUpdatesTable = ({ agent }) => { return ( - + { - it('A Linux agent should be well rendered.', () => { - const agent = { - os: { - uname: - 'Linux |ip-10-0-1-106 |4.9.0-9-amd64 |1 SMP Debian 4.9.168-1+deb9u2 (2019-05-13) |x86_64', - }, - }; - const wrapper = render(); - const networkPortsCard = wrapper.find('.euiFlexItem--flexGrow2').last(); - const networkPortsTitle = networkPortsCard - .find('.euiTitle.euiTitle--small') - .text(); - const networkPortsColumns = networkPortsCard.find('th'); +const TABLE_ID = '__table_7d62db31-1cd0-11ee-8e0c-33242698a3b9'; +const SOFTWARE_PACKAGES = 'Packages'; +const SOFTWARE_WINDOWS_UPDATES = 'Windows updates'; +const NETWORK_PORTS = 'Network ports'; +const NETWORK_INTERFACES = 'Network interfaces'; +const NETWORK_SETTINGS = 'Network settings'; +const PROCESSES = 'Processes'; +const AGENT = { + DEBIAN: { + os: { + uname: + 'Linux |ip-10-0-1-106 |4.9.0-9-amd64 |1 SMP Debian 4.9.168-1+deb9u2 (2019-05-13) |x86_64', + }, + }, + WINDOWS: { + os: { + platform: 'windows', + }, + }, + DARWIN: { + os: { + platform: 'darwin', + }, + }, +} as const; + +const NETWORK_PORTS_COLUMNS = { + LOCAL_PORT: 'Local port', + LOCAL_IP: 'Local IP address', + PROCESS: 'Process', + PID: 'PID', + STATE: 'State', + PROTOCOL: 'Protocol', +} as const; + +const NETWORK_INTERFACES_COLUMNS = { + NAME: 'Name', + MAC: 'MAC', + STATE: 'State', + MTU: 'MTU', + TYPE: 'Type', +} as const; + +const NETWORK_SETTINGS_COLUMNS = { + INTERFACE: 'Interface', + ADDRESS: 'Address', + NETMASK: 'Netmask', + PROTOCOL: 'Protocol', + BROADCAST: 'Broadcast', +} as const; + +const SOFTWARE_PACKAGES_COLUMNS = { + NAME: 'Name', + ARCHITECTURE: 'Architecture', + VENDOR: 'Vendor', + VERSION: 'Version', + FORMAT: 'Format', + LOCATION: 'Location', + DESCRIPTION: 'Description', +} as const; + +const SOFTWARE_WINDOWS_UPDATES_COLUMNS = { + UPDATE_CODE: 'Update code', +} as const; + +const PROCESSES_COLUMNS = { + NAME: 'Name', + EFFECTIVE_USER: 'Effective user', + EFFECTIVE_GROUP: 'Effective group', + PID: 'PID', + PARENT_PID: 'Parent PID', + COMMAND: 'Command', + ARGVS: 'Argvs', + SIZE: 'Size', + VM_SIZE: 'VM size', + SESSION: 'Session', + PRIORITY: 'Priority', + STATE: 'State', + NLWP: 'NLWP', +} as const; + +const shouldRenderTableWithCorrectColumnsAndTitle = ( + dataTestId: string, + title: string, + agentTab: (typeof AgentTabs)[keyof typeof AgentTabs], + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + const wrapper = render( + , + ); + const visTable = wrapper.find(queryDataTestAttr(dataTestId)).first(); + const visTitle = visTable + .find(queryDataTestAttr('table-wz-api-title')) + .text(); + const visColumns = visTable.find( + queryDataTestAttr('table-with-search-bar') + ' .euiTableHeaderCell', + ); + + const visTables = wrapper.find('table'); + + for (let i = 0; i < visTables.length; i++) { // This is done because the ID of the tables changes at each execution and breaks the snapshot. + visTables[i]['attribs']['id'] = TABLE_ID; + } + + expect(wrapper).toMatchSnapshot(); + expect(visTitle.trim()).toContain(title); + expect(visColumns.length).toEqual(columns.length); + for (let i = 0; i < columns.length; i++) { + expect(visColumns.eq(i).text()).toContain(columns[i]); + } +}; +const shouldRenderNetworkPortsTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'network-ports-table', + NETWORK_PORTS, + AgentTabs.NETWORK, + agent, + columns, + ); +}; + +const shouldRenderNetworkInterfacesTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'network-interfaces-table', + NETWORK_INTERFACES, + AgentTabs.NETWORK, + agent, + columns, + ); +}; + +const shouldRenderNetworkSettingsTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'network-settings-table', + NETWORK_SETTINGS, + AgentTabs.NETWORK, + agent, + columns, + ); +}; + +const shouldRenderSoftwarePackagesTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'software-packages-table', + SOFTWARE_PACKAGES, + AgentTabs.SOFTWARE, + agent, + columns, + ); +}; + +const shouldRenderSoftwareWindowsUpdatesTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'software-windows-updates-table', + SOFTWARE_WINDOWS_UPDATES, + AgentTabs.SOFTWARE, + agent, + columns, + ); +}; + +const shouldRenderProcessesTableWithCorrectColumnsAndTitle = ( + agent: (typeof AGENT)[keyof typeof AGENT], + columns: string[], +) => { + shouldRenderTableWithCorrectColumnsAndTitle( + 'processes-table', + PROCESSES, + AgentTabs.PROCESSES, + agent, + columns, + ); +}; + +function findAgentInfo(wrapper: cheerio.Cheerio): any { + return wrapper.find(queryDataTestAttr('agent-info')).html(); +} + +describe('Inventory data', () => { + describe('Agent info', () => { + it("A Linux agent shouldn't render agent info", () => { + let wrapper = render( + , + ); + + let agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + }); + + it("A Windows agent shouldn't render agent info", () => { + let wrapper = render( + , + ); + + let agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + }); - const tableId = '__table_7d62db31-1cd0-11ee-8e0c-33242698a3b9'; - - const tables = wrapper.find('table'); - - for (let i = 0; i < tables.length; i++) { - tables[i]['attribs']['id'] = tableId; - } - - const columns = [ - 'Local port', - 'Local IP address', - 'Process', - 'PID', - 'State', - 'Protocol', - ]; - - expect(wrapper).toMatchSnapshot(); - expect(networkPortsTitle.trim()).toContain('Network ports'); - expect(networkPortsColumns.length).toEqual(columns.length); - for (let i = 0; i < columns.length; i++) { - expect(networkPortsColumns.eq(i).text()).toContain(columns[i]); - } + it("A Apple agent shouldn't render agent info", () => { + let wrapper = render( + , + ); + + let agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + + wrapper = render( + , + ); + + agentInfo = findAgentInfo(wrapper); + + expect(agentInfo).toBeFalsy(); + }); }); - it('A Windows agent should be well rendered.', () => { - const agent = { - os: { - platform: 'windows', - }, - }; - const wrapper = render(); - const networkPortsCard = wrapper.find('.euiFlexItem--flexGrow2').last(); - const networkPortsTitle = networkPortsCard - .find('.euiTitle.euiTitle--small') - .text(); - const networkPortsColumns = networkPortsCard.find('th'); + describe('Software', () => { + it('should render inventory metrics', () => { + const wrapper = render( + , + ); - // This is done because the ID of the tables changes at each execution and breaks the snapshot. + const inventoryMetrics = wrapper.find( + queryDataTestAttr('syscollector-metrics'), + ); - const tableId = '__table_7d62db31-1cd0-11ee-8e0c-33242698a3b9'; + expect(inventoryMetrics).toBeTruthy(); + }); - const tables = wrapper.find('table'); + describe(SOFTWARE_PACKAGES + ' table', () => { + it('A Linux agent should render software table with correct columns and title.', () => { + const columns = [ + SOFTWARE_PACKAGES_COLUMNS.NAME, + SOFTWARE_PACKAGES_COLUMNS.ARCHITECTURE, + SOFTWARE_PACKAGES_COLUMNS.VERSION, + SOFTWARE_PACKAGES_COLUMNS.VENDOR, + SOFTWARE_PACKAGES_COLUMNS.DESCRIPTION, + ]; - for (let i = 0; i < tables.length; i++) { - tables[i]['attribs']['id'] = tableId; - } + shouldRenderSoftwarePackagesTableWithCorrectColumnsAndTitle( + AGENT.DEBIAN, + columns, + ); + }); + it('A Windows agent should render software table with correct columns and title.', () => { + const columns = [ + SOFTWARE_PACKAGES_COLUMNS.NAME, + SOFTWARE_PACKAGES_COLUMNS.ARCHITECTURE, + SOFTWARE_PACKAGES_COLUMNS.VERSION, + SOFTWARE_PACKAGES_COLUMNS.VENDOR, + ]; - const columns = [ - 'Local port', - 'Local IP address', - 'Process', - 'State', - 'Protocol', - ]; + shouldRenderSoftwarePackagesTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); + it('A Apple agent should render software table with correct columns and title.', () => { + const columns = [ + SOFTWARE_PACKAGES_COLUMNS.NAME, + SOFTWARE_PACKAGES_COLUMNS.VERSION, + SOFTWARE_PACKAGES_COLUMNS.FORMAT, + SOFTWARE_PACKAGES_COLUMNS.LOCATION, + SOFTWARE_PACKAGES_COLUMNS.DESCRIPTION, + ]; - expect(wrapper).toMatchSnapshot(); - expect(networkPortsTitle.trim()).toContain('Network ports'); - expect(networkPortsColumns.length).toEqual(columns.length); - for (let i = 0; i < columns.length; i++) { - expect(networkPortsColumns.eq(i).text()).toContain(columns[i]); - } + shouldRenderSoftwarePackagesTableWithCorrectColumnsAndTitle( + AGENT.DARWIN, + columns, + ); + }); + }); + + describe(SOFTWARE_WINDOWS_UPDATES + ' table', () => { + it('A Linux agent should render software table with correct columns and title.', () => { + const wrapper = render( + , + ); + const visTable = wrapper + .find(queryDataTestAttr('software-windows-updates-table]')) + .first() + .html(); + + expect(visTable).toBeFalsy(); + }); + + it('A Windows agent should render software table with correct columns and title.', () => { + const columns = [SOFTWARE_WINDOWS_UPDATES_COLUMNS.UPDATE_CODE]; + + shouldRenderSoftwareWindowsUpdatesTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); + + it('A Apple agent should render software table with correct columns and title.', () => { + const wrapper = render( + , + ); + const visTable = wrapper + .find(queryDataTestAttr('software-windows-updates-table')) + .first() + .html(); + + expect(visTable).toBeFalsy(); + }); + }); }); - it('A Apple agent should be well rendered.', () => { - const agent = { - os: { - platform: 'darwin', - }, - }; - const wrapper = render(); - const networkPortsCard = wrapper.find('.euiFlexItem--flexGrow2').last(); - const networkPortsTitle = networkPortsCard - .find('.euiTitle.euiTitle--small') - .text(); - const networkPortsColumns = networkPortsCard.find('th'); + describe('Network', () => { + it('should render inventory metrics', () => { + const wrapper = render( + , + ); - // This is done because the ID of the tables changes at each execution and breaks the snapshot. + const inventoryMetrics = wrapper.find( + queryDataTestAttr('syscollector-metrics'), + ); + + expect(inventoryMetrics).toBeTruthy(); + }); + + describe(NETWORK_SETTINGS + ' table', () => { + it('A Linux agent should render network settings table with correct columns and title.', () => { + const columns = [ + NETWORK_SETTINGS_COLUMNS.INTERFACE, + NETWORK_SETTINGS_COLUMNS.ADDRESS, + NETWORK_SETTINGS_COLUMNS.NETMASK, + NETWORK_SETTINGS_COLUMNS.PROTOCOL, + NETWORK_SETTINGS_COLUMNS.BROADCAST, + ]; + + shouldRenderNetworkSettingsTableWithCorrectColumnsAndTitle( + AGENT.DEBIAN, + columns, + ); + }); + it('A Windows agent should render network settings table with correct columns and title.', () => { + const columns = [ + NETWORK_SETTINGS_COLUMNS.INTERFACE, + NETWORK_SETTINGS_COLUMNS.ADDRESS, + NETWORK_SETTINGS_COLUMNS.NETMASK, + NETWORK_SETTINGS_COLUMNS.PROTOCOL, + NETWORK_SETTINGS_COLUMNS.BROADCAST, + ]; + + shouldRenderNetworkSettingsTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); + it('A Apple agent should render network settings table with correct columns and title.', () => { + const columns = [ + NETWORK_SETTINGS_COLUMNS.INTERFACE, + NETWORK_SETTINGS_COLUMNS.ADDRESS, + NETWORK_SETTINGS_COLUMNS.NETMASK, + NETWORK_SETTINGS_COLUMNS.PROTOCOL, + NETWORK_SETTINGS_COLUMNS.BROADCAST, + ]; + + shouldRenderNetworkSettingsTableWithCorrectColumnsAndTitle( + AGENT.DARWIN, + columns, + ); + }); + }); + describe(NETWORK_INTERFACES + ' table', () => { + it('A Linux agent should render network interfaces table with correct columns and title.', () => { + const columns = [ + NETWORK_INTERFACES_COLUMNS.NAME, + NETWORK_INTERFACES_COLUMNS.MAC, + NETWORK_INTERFACES_COLUMNS.STATE, + NETWORK_INTERFACES_COLUMNS.MTU, + NETWORK_INTERFACES_COLUMNS.TYPE, + ]; + + shouldRenderNetworkInterfacesTableWithCorrectColumnsAndTitle( + AGENT.DEBIAN, + columns, + ); + }); + it('A Windows agent should render network interfaces table with correct columns and title.', () => { + const columns = [ + NETWORK_INTERFACES_COLUMNS.NAME, + NETWORK_INTERFACES_COLUMNS.MAC, + NETWORK_INTERFACES_COLUMNS.STATE, + NETWORK_INTERFACES_COLUMNS.MTU, + NETWORK_INTERFACES_COLUMNS.TYPE, + ]; + + shouldRenderNetworkInterfacesTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); + it('A Apple agent should render network interfaces table with correct columns and title.', () => { + const columns = [ + NETWORK_INTERFACES_COLUMNS.NAME, + NETWORK_INTERFACES_COLUMNS.MAC, + NETWORK_INTERFACES_COLUMNS.STATE, + NETWORK_INTERFACES_COLUMNS.MTU, + NETWORK_INTERFACES_COLUMNS.TYPE, + ]; + + shouldRenderNetworkInterfacesTableWithCorrectColumnsAndTitle( + AGENT.DARWIN, + columns, + ); + }); + }); + describe(NETWORK_PORTS + ' table', () => { + it('A Linux agent should render network ports table with correct columns and title.', () => { + const columns = [ + NETWORK_PORTS_COLUMNS.LOCAL_PORT, + NETWORK_PORTS_COLUMNS.LOCAL_IP, + NETWORK_PORTS_COLUMNS.PROCESS, + NETWORK_PORTS_COLUMNS.PID, + NETWORK_PORTS_COLUMNS.STATE, + NETWORK_PORTS_COLUMNS.PROTOCOL, + ]; + + shouldRenderNetworkPortsTableWithCorrectColumnsAndTitle( + AGENT.DEBIAN, + columns, + ); + }); + + it('A Windows agent should render network ports table with correct columns and title.', () => { + const columns = [ + NETWORK_PORTS_COLUMNS.LOCAL_PORT, + NETWORK_PORTS_COLUMNS.LOCAL_IP, + NETWORK_PORTS_COLUMNS.PROCESS, + NETWORK_PORTS_COLUMNS.STATE, + NETWORK_PORTS_COLUMNS.PROTOCOL, + ]; + shouldRenderNetworkPortsTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); + + it('A Apple agent should render network ports table with correct columns and title.', () => { + const columns = [ + NETWORK_PORTS_COLUMNS.LOCAL_PORT, + NETWORK_PORTS_COLUMNS.LOCAL_IP, + NETWORK_PORTS_COLUMNS.STATE, + NETWORK_PORTS_COLUMNS.PROTOCOL, + ]; + + shouldRenderNetworkPortsTableWithCorrectColumnsAndTitle( + AGENT.DARWIN, + columns, + ); + }); + }); + }); + + describe('Processes', () => { + it('should render inventory metrics', () => { + const wrapper = render( + , + ); + + const inventoryMetrics = wrapper.find( + queryDataTestAttr('syscollector-metrics'), + ); + + expect(inventoryMetrics).toBeTruthy(); + }); + + it('A Linux agent should render processes table with correct columns and title.', () => { + const columns = [ + PROCESSES_COLUMNS.NAME, + PROCESSES_COLUMNS.EFFECTIVE_USER, + PROCESSES_COLUMNS.EFFECTIVE_GROUP, + PROCESSES_COLUMNS.PID, + PROCESSES_COLUMNS.PARENT_PID, + PROCESSES_COLUMNS.COMMAND, + PROCESSES_COLUMNS.ARGVS, + PROCESSES_COLUMNS.VM_SIZE, + PROCESSES_COLUMNS.SIZE, + PROCESSES_COLUMNS.SESSION, + PROCESSES_COLUMNS.PRIORITY, + PROCESSES_COLUMNS.STATE, + ]; - const tableId = '__table_7d62db31-1cd0-11ee-8e0c-33242698a3b9'; + shouldRenderProcessesTableWithCorrectColumnsAndTitle( + AGENT.DEBIAN, + columns, + ); + }); - const tables = wrapper.find('table'); + it('A Windows agent should render processes table with correct columns and title.', () => { + const columns = [ + PROCESSES_COLUMNS.NAME, + PROCESSES_COLUMNS.PID, + PROCESSES_COLUMNS.PARENT_PID, + PROCESSES_COLUMNS.VM_SIZE, + PROCESSES_COLUMNS.PRIORITY, + PROCESSES_COLUMNS.NLWP, + PROCESSES_COLUMNS.COMMAND, + ]; - for (let i = 0; i < tables.length; i++) { - tables[i]['attribs']['id'] = tableId; - } + shouldRenderProcessesTableWithCorrectColumnsAndTitle( + AGENT.WINDOWS, + columns, + ); + }); - const columns = ['Local port', 'Local IP address', 'State', 'Protocol']; + it('A Apple agent should render processes table with correct columns and title.', () => { + const columns = [ + PROCESSES_COLUMNS.NAME, + PROCESSES_COLUMNS.EFFECTIVE_USER, + PROCESSES_COLUMNS.PID, + PROCESSES_COLUMNS.PARENT_PID, + PROCESSES_COLUMNS.VM_SIZE, + PROCESSES_COLUMNS.PRIORITY, + ]; - expect(wrapper).toMatchSnapshot(); - expect(networkPortsTitle.trim()).toContain('Network ports'); - expect(networkPortsColumns.length).toEqual(columns.length); - for (let i = 0; i < columns.length; i++) { - expect(networkPortsColumns.eq(i).text()).toContain(columns[i]); - } + shouldRenderProcessesTableWithCorrectColumnsAndTitle( + AGENT.DARWIN, + columns, + ); + }); }); }); diff --git a/plugins/main/public/components/agents/syscollector/inventory.tsx b/plugins/main/public/components/agents/syscollector/inventory.tsx index 8f5e2413c7..417db55c4c 100644 --- a/plugins/main/public/components/agents/syscollector/inventory.tsx +++ b/plugins/main/public/components/agents/syscollector/inventory.tsx @@ -11,20 +11,22 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; -import { InventoryMetrics } from './components/syscollector-metrics'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiCallOut, + EuiSpacer, + EuiPage, + EuiPageBody, +} from '@elastic/eui'; import { API_NAME_AGENT_STATUS } from '../../../../common/constants'; import { compose } from 'redux'; import { withGuard } from '../../common/hocs'; import { PromptAgentNeverConnected } from '../prompts'; -import { - NetworkInterfacesTable, - NetworkPortsTable, - NetworkSettingsTable, - WindowsUpdatesTable, - ProcessesTable, - PackagesTable, -} from './components'; +import SoftwareTab from './software'; +import NetworkTab from './network'; +import ProcessesTab from './processes'; +import { InventoryMetrics } from './components'; export const SyscollectorInventory = compose( withGuard( @@ -33,7 +35,8 @@ export const SyscollectorInventory = compose( props.agent.status === API_NAME_AGENT_STATUS.NEVER_CONNECTED, PromptAgentNeverConnected, ), -)(function SyscollectorInventory({ agent }) { +)(function SyscollectorInventory(props) { + const { agent, section } = props; let soPlatform; if (agent?.os?.uname?.includes('Linux')) { soPlatform = 'linux'; @@ -48,54 +51,33 @@ export const SyscollectorInventory = compose( } return ( -
- {agent && agent.status === API_NAME_AGENT_STATUS.DISCONNECTED && ( - - - - - - )} - - - - - - - - - - - - - - - - - - - - {agent && agent.os && agent.os.platform === 'windows' && ( - - - + + + {agent?.status === API_NAME_AGENT_STATUS.DISCONNECTED && ( + + + + + )} - - - - - - + - - - - - -
+ + + {section === 'software' && ( + + )} + {section === 'network' && ( + + )} + {section === 'processes' && ( + + )} + + ); }); diff --git a/plugins/main/public/components/agents/syscollector/network.tsx b/plugins/main/public/components/agents/syscollector/network.tsx new file mode 100644 index 0000000000..db99e84f00 --- /dev/null +++ b/plugins/main/public/components/agents/syscollector/network.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { + NetworkInterfacesTable, + NetworkPortsTable, + NetworkSettingsTable, +} from './components'; +import { Agent } from '../../endpoints-summary/types'; + +interface NetworkProps { + agent: Agent; + soPlatform?: string; +} + +const NetworkTab = ({ agent, soPlatform }: NetworkProps) => { + return ( + + + + + + + + + + + + + + ); +}; + +export default NetworkTab; diff --git a/plugins/main/public/components/agents/syscollector/processes.tsx b/plugins/main/public/components/agents/syscollector/processes.tsx new file mode 100644 index 0000000000..8403be456c --- /dev/null +++ b/plugins/main/public/components/agents/syscollector/processes.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { ProcessesTable } from './components'; +import { Agent } from '../../endpoints-summary/types'; + +interface ProcessesProps { + agent: Agent; + soPlatform?: string; +} + +const ProcessesTab = ({ agent, soPlatform }: ProcessesProps) => { + return ( + + + + + + ); +}; + +export default ProcessesTab; diff --git a/plugins/main/public/components/agents/syscollector/software.tsx b/plugins/main/public/components/agents/syscollector/software.tsx new file mode 100644 index 0000000000..a8c8f1db27 --- /dev/null +++ b/plugins/main/public/components/agents/syscollector/software.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { Agent } from '../../endpoints-summary/types'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { PackagesTable, WindowsUpdatesTable } from './components'; + +interface SoftwareProps { + agent: Agent; + soPlatform?: string; +} + +const SoftwareTab = ({ agent, soPlatform }: SoftwareProps) => { + return ( + + {agent?.os?.platform === 'windows' && ( + + + + )} + + + + + ); +}; + +export default SoftwareTab; diff --git a/plugins/main/public/components/common/hooks/use_async_action.ts b/plugins/main/public/components/common/hooks/use_async_action.ts index 24ab181926..a98495fa9c 100644 --- a/plugins/main/public/components/common/hooks/use_async_action.ts +++ b/plugins/main/public/components/common/hooks/use_async_action.ts @@ -11,24 +11,27 @@ */ import { useCallback, useState } from 'react'; -export function useAsyncAction(action, dependencies = []){ +export function useAsyncAction( + action: (...params: any[]) => any, + dependencies: any[] = [], +) { const [running, setRunning] = useState(false); const [data, setData] = useState(null); const [error, setError] = useState(null); const run = useCallback(async (...params) => { - try{ + try { setRunning(true); setError(null); setData(null); const data = await action(...params); setData(data); - }catch(error){ + } catch (error) { setError(error); - }finally{ + } finally { setRunning(false); - }; + } }, dependencies); return { data, error, running, run }; -} \ No newline at end of file +} diff --git a/plugins/main/public/components/common/modules/main-agent.test.tsx b/plugins/main/public/components/common/modules/main-agent.test.tsx new file mode 100644 index 0000000000..02ba68cd67 --- /dev/null +++ b/plugins/main/public/components/common/modules/main-agent.test.tsx @@ -0,0 +1,359 @@ +import React from 'react'; +import { MainModuleAgent } from './main-agent'; +import { AgentTabs } from '../../endpoints-summary/agent/agent-tabs'; +import { fireEvent, render } from '@testing-library/react'; +import { queryDataTestAttr } from '../../../../test/public/query-attr'; + +const GENERATE_REPORT_BUTTON = 'generate-report-button'; +const ARIA_SELECTED = '[aria-selected="true"]'; + +const REPORT_TAB = { + STATS: 'agent-tab-stats', + CONFIGURATION: 'agent-tab-configuration', + SOFTWARE: 'agent-tab-software', + NETWORK: 'agent-tab-network', + PROCESSES: 'agent-tab-processes', +}; + +jest.mock('../../../react-services/reporting', () => ({ + ReportingService: { + startVis2Png: jest.fn(), + startConfigReport: jest.fn(), + }, +})); + +jest.mock('../data-source', () => ({ + useDataSource: jest.fn().mockImplementation(() => ({ + dataSource: {}, + fetchFilters: {}, + isLoading: false, + })), + AlertsDataSourceRepository: jest.fn(), + AlertsDataSource: jest.fn(), + __esModule: true, +})); + +describe('Main Agent', () => { + let switchTab: jest.Mock; + + beforeEach(() => { + switchTab = jest.fn(); + }); + + describe('Agent tabs', () => { + it('should render agent tab overview when section is stats', () => { + const { container } = render( + , + ); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.STATS) + ARIA_SELECTED, + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.CONFIGURATION)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.SOFTWARE)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.NETWORK)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.PROCESSES)), + ).toBeFalsy(); + }); + + it('should render agent tab overview when section is configuration', () => { + const { container } = render( + , + ); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.CONFIGURATION) + ARIA_SELECTED, + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.STATS)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.SOFTWARE)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.NETWORK)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.PROCESSES)), + ).toBeFalsy(); + }); + + it('should render agent tab overview when section is software', () => { + const { container } = render( + , + ); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.SOFTWARE) + ARIA_SELECTED, + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.NETWORK)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.NETWORK) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.PROCESSES)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.PROCESSES) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.CONFIGURATION)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.STATS)), + ).toBeFalsy(); + }); + + it('should render agent tab overview when section is network', () => { + const { container } = render( + , + ); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.SOFTWARE)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.SOFTWARE) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.NETWORK) + ARIA_SELECTED, + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.PROCESSES)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.PROCESSES) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.CONFIGURATION)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.STATS)), + ).toBeFalsy(); + }); + + it('should render agent tab overview when section is processes', () => { + const { container } = render( + , + ); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.SOFTWARE)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.SOFTWARE) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.NETWORK)), + ).toBeTruthy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.NETWORK) + ARIA_SELECTED, + ), + ).toBeFalsy(); + + expect( + container.querySelector( + queryDataTestAttr(REPORT_TAB.PROCESSES) + ARIA_SELECTED, + ), + ).toBeTruthy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.CONFIGURATION)), + ).toBeFalsy(); + + expect( + container.querySelector(queryDataTestAttr(REPORT_TAB.STATS)), + ).toBeFalsy(); + }); + + it('should be call switchTab when click on tab', () => { + const { container } = render( + , + ); + + fireEvent.click( + container.querySelector( + queryDataTestAttr(REPORT_TAB.SOFTWARE), + ) as Element, + ); + + expect(switchTab).toHaveBeenCalledTimes(1); + expect(switchTab).toHaveBeenCalledWith(AgentTabs.SOFTWARE); + + fireEvent.click( + container.querySelector( + queryDataTestAttr(REPORT_TAB.NETWORK), + ) as Element, + ); + + expect(switchTab).toHaveBeenCalledTimes(2); + expect(switchTab).toHaveBeenCalledWith(AgentTabs.NETWORK); + + fireEvent.click( + container.querySelector( + queryDataTestAttr(REPORT_TAB.PROCESSES), + ) as Element, + ); + + expect(switchTab).toHaveBeenCalledTimes(3); + expect(switchTab).toHaveBeenCalledWith(AgentTabs.PROCESSES); + }); + }); + + describe('Generate report button', () => { + it("shouldn't render generate report button when section is stats", () => { + const { container } = render( + , + ); + + const generateReportButton = container.querySelector( + queryDataTestAttr(GENERATE_REPORT_BUTTON), + ); + + expect(generateReportButton).toBeFalsy(); + }); + + it("shouldn't render generate report button when section is configuration", () => { + const { container } = render( + , + ); + + const generateReportButton = container.querySelector( + queryDataTestAttr(GENERATE_REPORT_BUTTON), + ); + + expect(generateReportButton).toBeFalsy(); + }); + + it('should render generate report button when section is software', () => { + const { container } = render( + , + ); + + const generateReportButton = container.querySelector( + queryDataTestAttr(GENERATE_REPORT_BUTTON), + ); + + expect(generateReportButton).toBeTruthy(); + }); + + it('should render generate report button when section is network', () => { + const { container } = render( + , + ); + + const generateReportButton = container.querySelector( + queryDataTestAttr(GENERATE_REPORT_BUTTON), + ); + + expect(generateReportButton).toBeTruthy(); + }); + + it('should render generate report button when section is processes', () => { + const { container } = render( + , + ); + + const generateReportButton = container.querySelector( + queryDataTestAttr(GENERATE_REPORT_BUTTON), + ); + + expect(generateReportButton).toBeTruthy(); + }); + }); +}); diff --git a/plugins/main/public/components/common/modules/main-agent.tsx b/plugins/main/public/components/common/modules/main-agent.tsx index b7d477038f..74ebf831e0 100644 --- a/plugins/main/public/components/common/modules/main-agent.tsx +++ b/plugins/main/public/components/common/modules/main-agent.tsx @@ -14,20 +14,13 @@ import React, { Component, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, - EuiTitle, EuiButtonEmpty, + EuiTabs, + EuiTab, } from '@elastic/eui'; -import { euiThemeVars } from '@osd/ui-shared-deps/theme'; import '../../common/modules/module.scss'; import store from '../../../redux/store'; -import { FilterHandler } from '../../../utils/filter-handler'; -import { AppState } from '../../../react-services/app-state'; import { ReportingService } from '../../../react-services/reporting'; -import { WAZUH_MODULES } from '../../../../common/wazuh-modules'; -import { AgentInfo } from '../../common/welcome/agents-info'; -import { compose } from 'redux'; -import { withGlobalBreadcrumb } from '../hocs'; -import { endpointSummary } from '../../../utils/applications'; import { AlertsDataSource, AlertsDataSourceRepository, @@ -37,140 +30,103 @@ import { useDataSource, } from '../data-source'; import { useAsyncAction } from '../hooks'; -import NavigationService from '../../../react-services/navigation-service'; +import { toTitleCase } from '../util/change-case'; +import clsx from 'clsx'; +import { AgentTabs } from '../../endpoints-summary/agent/agent-tabs'; +import { Agent } from '../../endpoints-summary/types'; export class MainModuleAgent extends Component { props!: { - [key: string]: any; + agent: Agent; + section: string; + switchTab?: (tab: string) => void; + selectView?: boolean; + tabs?: any[]; + renderTabs?: () => JSX.Element; + agentsSelectionProps?: any; }; - state: { - selectView: Boolean; - loadingReport: Boolean; - switchModule: Boolean; - showAgentInfo: Boolean; - }; - reportingService: ReportingService; - filterHandler: FilterHandler; - constructor(props) { - super(props); - this.reportingService = new ReportingService(); - this.filterHandler = new FilterHandler(AppState.getCurrentPattern()); - this.state = { - selectView: false, - loadingReport: false, - switchModule: false, - showAgentInfo: false, - }; - } + inventoryTabs = [AgentTabs.SOFTWARE, AgentTabs.NETWORK, AgentTabs.PROCESSES]; renderTitle() { + const { agent, section, switchTab } = this.props; return ( - - - - - - -

- { - NavigationService.getInstance().navigate( - `/agents?tab=welcome&agent=${this.props.agent.id}`, - ); - }} - > - -  {this.props.agent.name}    - - -

-
-
-
- - {this.props.section === 'syscollector' && ( - - - + + + + {this.inventoryTabs.includes(section) ? ( + <> + {this.inventoryTabs.map(tab => ( + switchTab?.(tab)} + > + {toTitleCase(tab)} + + ))} + + ) : ( + + {toTitleCase(section)} + )} - + + + {[AgentTabs.SOFTWARE, AgentTabs.NETWORK, AgentTabs.PROCESSES].includes( + section, + ) && ( + + + + )}
); } render() { const { agent, section, selectView } = this.props; - const ModuleTabView = (this.props.tabs || []).find( - tab => tab.id === selectView, - ); + const ModuleTabView = this.props.tabs?.find(tab => tab.id === selectView); + + const hasTabs = this.props.tabs?.length; + return ( -
- {agent && agent.os && ( +
+ {agent?.os && (
{this.renderTitle()}
-
- {this.state.showAgentInfo && ( -
- -
- )} - {this.props.tabs && this.props.tabs.length && ( +
+ {hasTabs && (
- {this.props.renderTabs()} + {this.props.renderTabs?.()} - {ModuleTabView && - ModuleTabView.buttons && - ModuleTabView.buttons.map( - (ModuleViewButton, index) => - typeof ModuleViewButton !== 'string' ? ( - - - - ) : null, - )} + {ModuleTabView?.buttons?.map( + (ModuleViewButton, index) => + typeof ModuleViewButton !== 'string' ? ( + + + + ) : null, + )} @@ -178,9 +134,8 @@ export class MainModuleAgent extends Component { )}
- {!['syscollector', 'configuration'].includes(section) && - ModuleTabView && - ModuleTabView.component && ( + {[AgentTabs.STATS].includes(section) && + ModuleTabView?.component && ( )} @@ -190,41 +145,6 @@ export class MainModuleAgent extends Component { } } -export default compose( - withGlobalBreadcrumb(({ agent, section }) => { - if (section === 'welcome') { - return [ - { - text: endpointSummary.breadcrumbLabel, - href: NavigationService.getInstance().getUrlForApp( - endpointSummary.id, - { - path: `#/agents-preview`, - }, - ), - }, - { text: agent.id }, - ]; - } else { - return [ - { - text: endpointSummary.breadcrumbLabel, - href: NavigationService.getInstance().getUrlForApp( - endpointSummary.id, - { - path: `#/agents-preview`, - }, - ), - }, - { agent: agent }, - { - text: WAZUH_MODULES[section].title, - }, - ]; - } - }), -)(MainModuleAgent); - export class AgentInventoryDataSource extends AlertsDataSource { constructor(id: string, title: string) { super(id, title); @@ -238,7 +158,7 @@ export class AgentInventoryDataSource extends AlertsDataSource { } } -const GenerateSyscollectorReportButton = ({ agent }) => { +const GenerateReportButton = ({ agent }: { agent: Agent }) => { const { dataSource, fetchFilters, @@ -254,7 +174,7 @@ const GenerateSyscollectorReportButton = ({ agent }) => { (agent || store.getState().appStateReducers.currentAgentData || {}).id || false; await reportingService.startVis2Png('syscollector', agentID, { - indexPattern: dataSource.indexPattern, + indexPattern: dataSource?.indexPattern, query: { query: '', language: 'kuery' }, filters: fetchFilters, time: { @@ -266,6 +186,7 @@ const GenerateSyscollectorReportButton = ({ agent }) => { return ( {ModuleTabView && diff --git a/plugins/main/public/components/common/modules/module.scss b/plugins/main/public/components/common/modules/module.scss index 8182b3d601..dbbd4491f1 100644 --- a/plugins/main/public/components/common/modules/module.scss +++ b/plugins/main/public/components/common/modules/module.scss @@ -7,14 +7,9 @@ background: #fafbfd; } -.wz-module-header-agent { - height: 50px; - padding: 16px; -} - -.wz-module-header-agent .euiHealth svg { - width: 16px; - height: 24px; +.wz-module-header-agent .euiTab__content { + font-size: 16px !important; + font-weight: 400; } .wzApp .euiFlyoutBody .euiFlyoutBody__overflowContent { @@ -25,6 +20,10 @@ margin-top: -16px; } +.wz-module-header-agent > .euiFlexGroup { + margin-top: -6px; +} + .wz-module-header-nav > .euiFlexGroup .euiFlexItem { margin-top: 0px; } @@ -39,20 +38,9 @@ border-radius: 100px; } -.wz-module-header-agent-title-btn { - cursor: pointer; -} - .wz-font-weight-normal { font-weight: normal; } -.wz-module-header-agent h1 { - font-weight: 400; -} - -.wz-module-header-agent h1 b { - font-weight: 500; -} .wz-module-header-nav .euiTabs { padding-top: 6px; @@ -96,11 +84,6 @@ discover-app-w .sidebar-container { @media only screen and (max-width: 767px) { .wz-module-header-agent { height: auto; - h1 { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - } } .wz-module-body { margin-top: -160px; diff --git a/plugins/main/public/components/common/modules/modules-defaults.tsx b/plugins/main/public/components/common/modules/modules-defaults.tsx index 0fc4b4b5bc..215c3dd14f 100644 --- a/plugins/main/public/components/common/modules/modules-defaults.tsx +++ b/plugins/main/public/components/common/modules/modules-defaults.tsx @@ -444,7 +444,13 @@ export const ModulesDefaults = { ], availableFor: ['manager', 'agent'], }, - syscollector: { + software: { + notModule: true, + }, + network: { + notModule: true, + }, + processes: { notModule: true, }, configuration: { diff --git a/plugins/main/public/components/common/ribbon/ribbon-item.scss b/plugins/main/public/components/common/ribbon/ribbon-item.scss new file mode 100644 index 0000000000..bf543a6a40 --- /dev/null +++ b/plugins/main/public/components/common/ribbon/ribbon-item.scss @@ -0,0 +1,4 @@ +.wz-ribbon-item .euiStat .euiText { + font-size: 12px; + font-family: sans-serif; +} diff --git a/plugins/main/public/components/common/ribbon/ribbon-item.tsx b/plugins/main/public/components/common/ribbon/ribbon-item.tsx new file mode 100644 index 0000000000..29741c8a6b --- /dev/null +++ b/plugins/main/public/components/common/ribbon/ribbon-item.tsx @@ -0,0 +1,148 @@ +import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import React from 'react'; +import { WAZUH_AGENTS_OS_TYPE } from '../../../../common/constants'; +import { AgentStatus } from '../../agents/agent-status'; +import { Agent } from '../../endpoints-summary/types'; +import { WzStat } from '../../wz-stat'; +import { GroupTruncate } from '../util/agent-group-truncate'; +import WzTextWithTooltipIfTruncated from '../wz-text-with-tooltip-if-truncated'; +import './ribbon-item.scss'; +import { getAgentOSType } from '../../../react-services'; + +const FONT_SIZE = 12; + +export enum RibbonItemLabel { + GROUPS = 'groups', + OPERATING_SYSTEM = 'operating-system', + AGENT_STATUS = 'agent-status', +} + +export type IRibbonItem