From 0760cdd96031441c782d0ce481006cf0202960d2 Mon Sep 17 00:00:00 2001 From: Luciano Gorza <103193307+lucianogorza@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:19:06 -0300 Subject: [PATCH] Add upgrade agents global action (#6501) * Add upgrade agents global action * Add Upgrade result modal * Update CHANGELOG * Minor fixes * Add unit tests * Improve upgrade progress message * Remove wazuh-endpoints plugin * Fix file name * Fix import * Fix get tasks status for readonly user * Improve return condition * Improve error message for Timeout status * Add tooltipo to timeout status * Modularize get status functions * Fix status name and key values * Separate upgrade service in two services --- CHANGELOG.md | 4 +- docker/osd-dev/dev.yml | 1 - plugins/main/common/constants.ts | 9 + .../components/common/tables/table-wz-api.tsx | 12 +- .../components/outdated-agents-card.tsx | 4 +- .../endpoints-summary/endpoints-summary.scss | 3 +- .../endpoints-summary/hooks/upgrade-tasks.ts | 92 +++--- .../components/endpoints-summary/index.tsx | 2 +- .../services/add-agents-to-group.ts | 8 +- .../endpoints-summary/services/index.tsx | 1 + ....tsx => paginated-agents-request.test.tsx} | 14 +- ...group.tsx => paginated-agents-request.tsx} | 18 +- .../services/remove-agents-from-group.tsx | 8 +- .../services/upgrade-agent.tsx | 11 + .../services/upgrade-agents.tsx | 11 +- .../__snapshots__/agents-table.test.tsx.snap | 250 ++++++++--------- .../table/actions/actions.tsx | 7 +- .../table/actions/edit-groups-modal.tsx | 1 - .../actions/upgrade-agent-modal.test.tsx | 8 +- .../table/actions/upgrade-agent-modal.tsx | 122 ++++---- .../endpoints-summary/table/agents-table.tsx | 85 ++++-- .../endpoints-summary/table/columns.tsx | 2 + .../global-actions.test.tsx.snap | 2 +- .../global-actions/edit-groups/result.tsx | 7 +- .../global-actions/global-actions.test.tsx | 12 +- .../table/global-actions/global-actions.tsx | 67 ++++- .../__snapshots__/upgrade-modal.test.tsx.snap | 8 + .../table/global-actions/upgrade/result.tsx | 262 ++++++++++++++++++ .../upgrade/upgrade-modal.test.tsx | 77 +++++ .../global-actions/upgrade/upgrade-modal.tsx | 220 +++++++++++++++ .../table/upgrade-task-details-modal.tsx | 231 +++++++++++++++ .../upgrades-in-progress.test.tsx.snap | 86 ++++-- .../taskDetailsButton.tsx | 224 --------------- .../upgrades-in-progress.test.tsx | 26 +- .../upgrades-in-progress.tsx | 152 ++++++---- plugins/main/public/types.ts | 2 - plugins/wazuh-endpoints/.i18nrc.json | 7 - plugins/wazuh-endpoints/common/constants.ts | 4 - plugins/wazuh-endpoints/common/types.ts | 0 .../opensearch_dashboards.json | 9 - plugins/wazuh-endpoints/package.json | 20 -- plugins/wazuh-endpoints/public/index.ts | 8 - plugins/wazuh-endpoints/public/plugin.ts | 23 -- plugins/wazuh-endpoints/public/types.ts | 5 - plugins/wazuh-endpoints/scripts/jest.js | 19 -- plugins/wazuh-endpoints/scripts/manifest.js | 17 -- plugins/wazuh-endpoints/scripts/runner.js | 148 ---------- plugins/wazuh-endpoints/server/index.ts | 11 - plugins/wazuh-endpoints/server/plugin.ts | 37 --- plugins/wazuh-endpoints/server/types.ts | 10 - plugins/wazuh-endpoints/test/jest/config.js | 41 --- .../wazuh-endpoints/translations/en-US.json | 79 ------ plugins/wazuh-endpoints/tsconfig.json | 17 -- 53 files changed, 1436 insertions(+), 1068 deletions(-) rename plugins/main/public/components/endpoints-summary/services/{paginated-agents-group.test.tsx => paginated-agents-request.test.tsx} (93%) rename plugins/main/public/components/endpoints-summary/services/{paginated-agents-group.tsx => paginated-agents-request.tsx} (91%) create mode 100644 plugins/main/public/components/endpoints-summary/services/upgrade-agent.tsx create mode 100644 plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/__snapshots__/upgrade-modal.test.tsx.snap create mode 100644 plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/result.tsx create mode 100644 plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/upgrade-modal.test.tsx create mode 100644 plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/upgrade-modal.tsx create mode 100644 plugins/main/public/components/endpoints-summary/table/upgrade-task-details-modal.tsx delete mode 100644 plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/taskDetailsButton.tsx delete mode 100644 plugins/wazuh-endpoints/.i18nrc.json delete mode 100644 plugins/wazuh-endpoints/common/constants.ts delete mode 100644 plugins/wazuh-endpoints/common/types.ts delete mode 100644 plugins/wazuh-endpoints/opensearch_dashboards.json delete mode 100644 plugins/wazuh-endpoints/package.json delete mode 100644 plugins/wazuh-endpoints/public/index.ts delete mode 100644 plugins/wazuh-endpoints/public/plugin.ts delete mode 100644 plugins/wazuh-endpoints/public/types.ts delete mode 100644 plugins/wazuh-endpoints/scripts/jest.js delete mode 100644 plugins/wazuh-endpoints/scripts/manifest.js delete mode 100755 plugins/wazuh-endpoints/scripts/runner.js delete mode 100644 plugins/wazuh-endpoints/server/index.ts delete mode 100644 plugins/wazuh-endpoints/server/plugin.ts delete mode 100644 plugins/wazuh-endpoints/server/types.ts delete mode 100644 plugins/wazuh-endpoints/test/jest/config.js delete mode 100644 plugins/wazuh-endpoints/translations/en-US.json delete mode 100644 plugins/wazuh-endpoints/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bf6416e0..b1f91bd870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Added AngularJS dependencies [#6145](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6145) - Added a migration task to setup the configuration using a configuration file [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) - Added the ability to manage the API hosts from the Server APIs [#6337](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6337) [#6519](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6519) -- Added edit groups action to Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250) -- Added upgrade agent action to Endpoints Summary [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476) -- Added global actions add agents to groups and remove agents from groups to Endpoints Summary [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274) +- Added edit agent groups and upgrade agents actions to Endpoints Summary [#6250](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6250) [#6476](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6476) [#6274](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6274) [#6501](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6501) - Added propagation of updates from the table to dashboard visualizations in Endpoints summary [#6460](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6460) ### Changed diff --git a/docker/osd-dev/dev.yml b/docker/osd-dev/dev.yml index c13e118fe0..92c29e45af 100755 --- a/docker/osd-dev/dev.yml +++ b/docker/osd-dev/dev.yml @@ -245,7 +245,6 @@ services: - '${SRC}/main:/home/node/kbn/plugins/wazuh' - '${SRC}/wazuh-core:/home/node/kbn/plugins/wazuh-core' - '${SRC}/wazuh-check-updates:/home/node/kbn/plugins/wazuh-check-updates' - - '${SRC}/wazuh-endpoints:/home/node/kbn/plugins/wazuh-endpoints' - wd_certs:/home/node/kbn/certs/ - ${WAZUH_DASHBOARD_CONF}:/home/node/kbn/config/opensearch_dashboards.yml - ./config/${OSD_MAJOR}/osd/wazuh.yml:/home/node/kbn/data/wazuh/config/wazuh.yml diff --git a/plugins/main/common/constants.ts b/plugins/main/common/constants.ts index 8e0b1c9c93..d0a7c6539a 100644 --- a/plugins/main/common/constants.ts +++ b/plugins/main/common/constants.ts @@ -370,14 +370,23 @@ export const API_NAME_TASK_STATUS = { DONE: 'Done', IN_PROGRESS: 'In progress', FAILED: 'Failed', + TIMEOUT: 'Timeout', } as const; export const UI_TASK_STATUS = [ API_NAME_TASK_STATUS.DONE, API_NAME_TASK_STATUS.IN_PROGRESS, API_NAME_TASK_STATUS.FAILED, + API_NAME_TASK_STATUS.TIMEOUT, ]; +export const UI_TASK_STATUS_COLORS = { + [API_NAME_TASK_STATUS.DONE]: 'success', + [API_NAME_TASK_STATUS.IN_PROGRESS]: 'warning', + [API_NAME_TASK_STATUS.FAILED]: 'danger', + [API_NAME_TASK_STATUS.TIMEOUT]: 'subdued', +}; + // Documentation export const DOCUMENTATION_WEB_BASE_URL = 'https://documentation.wazuh.com'; diff --git a/plugins/main/public/components/common/tables/table-wz-api.tsx b/plugins/main/public/components/common/tables/table-wz-api.tsx index ff2b6f966f..14fb393f50 100644 --- a/plugins/main/public/components/common/tables/table-wz-api.tsx +++ b/plugins/main/public/components/common/tables/table-wz-api.tsx @@ -44,6 +44,7 @@ const getFilters = filters => { export function TableWzAPI({ actionButtons, + postActionButtons, addOnTitle, extra, setReload, @@ -53,6 +54,11 @@ export function TableWzAPI({ | ReactNode | ReactNode[] | (({ filters }: { filters }) => ReactNode); + postActionButtons?: + | ReactNode + | ReactNode[] + | (({ filters }: { filters }) => ReactNode); + title?: string; addOnTitle?: ReactNode; description?: string; @@ -147,7 +153,7 @@ export function TableWzAPI({ }, []); - const renderActionButtons = filters => { + const renderActionButtons = (actionButtons, filters) => { if (Array.isArray(actionButtons)) { return actionButtons.map((button, key) => ( @@ -216,7 +222,7 @@ export function TableWzAPI({ {/* Render optional custom action button */} - {renderActionButtons(filters)} + {renderActionButtons(actionButtons, filters)} {/* Render optional reload button */} {rest.showReload && ReloadButton} {/* Render optional export to CSV button */} @@ -232,6 +238,8 @@ export function TableWzAPI({ } /> )} + {/* Render optional post custom action button */} + {renderActionButtons(postActionButtons, filters)} {rest.showFieldSelector && ( diff --git a/plugins/main/public/components/endpoints-summary/dashboard/components/outdated-agents-card.tsx b/plugins/main/public/components/endpoints-summary/dashboard/components/outdated-agents-card.tsx index 304a05a8ab..6023a05de1 100644 --- a/plugins/main/public/components/endpoints-summary/dashboard/components/outdated-agents-card.tsx +++ b/plugins/main/public/components/endpoints-summary/dashboard/components/outdated-agents-card.tsx @@ -70,10 +70,10 @@ const OutdatedAgentsCard = ({ } description={ - Agents + {outdatedAgents === 1 ? 'Agent' : 'Agents'} } - titleColor='danger' + titleColor='warning' isLoading={isLoading} titleSize='l' textAlign='center' diff --git a/plugins/main/public/components/endpoints-summary/endpoints-summary.scss b/plugins/main/public/components/endpoints-summary/endpoints-summary.scss index f9e50aef92..04a2176409 100644 --- a/plugins/main/public/components/endpoints-summary/endpoints-summary.scss +++ b/plugins/main/public/components/endpoints-summary/endpoints-summary.scss @@ -106,7 +106,7 @@ width: 100%; display: grid; grid-template-columns: 1fr; - gap: 20px 10px; + gap: 16px; min-height: 200px; @media (min-width: 1024px) { @@ -114,7 +114,6 @@ } @media (min-width: 1440px) { - gap: 10px; grid-template-columns: minmax(375px, 1fr) minmax(375px, 1fr) minmax(375px, 1fr) minmax(150px, 300px); diff --git a/plugins/main/public/components/endpoints-summary/hooks/upgrade-tasks.ts b/plugins/main/public/components/endpoints-summary/hooks/upgrade-tasks.ts index f4ccd4a20a..8b0596f692 100644 --- a/plugins/main/public/components/endpoints-summary/hooks/upgrade-tasks.ts +++ b/plugins/main/public/components/endpoints-summary/hooks/upgrade-tasks.ts @@ -18,68 +18,83 @@ export const useGetUpgradeTasks = (reload: any) => { const [getErrorIsLoading, setErrorIsLoading] = useState(true); const [getErrorTasksError, setGetErrorTasksError] = useState(); + const [totalTimeoutUpgradeTasks, setTotalTimeoutUpgradeTasks] = + useState(0); + const [getTimeoutIsLoading, setTimeoutIsLoading] = useState(true); + const [getTimeoutError, setGetTimeoutError] = useState(); + const datetime = new Date(); datetime.setMinutes(datetime.getMinutes() - beforeMinutes); const formattedDate = datetime.toISOString(); const timeFilter = `last_update_time>${formattedDate}`; - const getUpgradesInProgress = async () => { + const getUpgradeStatus = async ( + status: string, + setIsLoading: (isLoading: boolean) => void, + setTotalTasks: (totalTasks: number) => void, + setError: (error) => void, + q?: string, + ) => { try { - setGetInProgressIsLoading(true); + setIsLoading(true); const { total_affected_items } = await getTasks({ - status: API_NAME_TASK_STATUS.IN_PROGRESS, + status, command: 'upgrade', limit: 1, + q, }); - setTotalInProgressTasks(total_affected_items); - setGetInProgressError(undefined); + setTotalTasks(total_affected_items); + setError(undefined); } catch (error: any) { - setGetInProgressError(error); + setError(error); } finally { - setGetInProgressIsLoading(false); + setIsLoading(false); } }; + const getUpgradesInProgress = async () => + await getUpgradeStatus( + API_NAME_TASK_STATUS.IN_PROGRESS, + setGetInProgressIsLoading, + setTotalInProgressTasks, + setGetInProgressError, + ); + const getUpgradesSuccess = async () => { - try { - setSuccessIsLoading(true); - const { total_affected_items } = await getTasks({ - status: API_NAME_TASK_STATUS.DONE, - command: 'upgrade', - limit: 1, - q: timeFilter, - }); - setTotalSuccessTasks(total_affected_items); - setGetSuccessError(undefined); - } catch (error: any) { - setGetSuccessError(error); - } finally { - setSuccessIsLoading(false); - } + await getUpgradeStatus( + API_NAME_TASK_STATUS.DONE, + setSuccessIsLoading, + setTotalSuccessTasks, + setGetSuccessError, + timeFilter, + ); }; const getUpgradesError = async () => { - try { - setErrorIsLoading(true); - const { total_affected_items } = await getTasks({ - status: API_NAME_TASK_STATUS.FAILED, - command: 'upgrade', - limit: 1, - q: timeFilter, - }); - setTotalErrorUpgradeTasks(total_affected_items); - setGetErrorTasksError(undefined); - } catch (error: any) { - setGetErrorTasksError(error); - } finally { - setErrorIsLoading(false); - } + await getUpgradeStatus( + API_NAME_TASK_STATUS.FAILED, + setErrorIsLoading, + setTotalErrorUpgradeTasks, + setGetErrorTasksError, + timeFilter, + ); + }; + + const getUpgradeTimeout = async () => { + await getUpgradeStatus( + API_NAME_TASK_STATUS.TIMEOUT, + setTimeoutIsLoading, + setTotalTimeoutUpgradeTasks, + setGetTimeoutError, + timeFilter, + ); }; const fetchData = async () => { await getUpgradesInProgress(); await getUpgradesSuccess(); await getUpgradesError(); + await getUpgradeTimeout(); }; useEffect(() => { @@ -104,5 +119,8 @@ export const useGetUpgradeTasks = (reload: any) => { getErrorIsLoading, totalErrorUpgradeTasks, getErrorTasksError, + getTimeoutIsLoading, + totalTimeoutUpgradeTasks, + getTimeoutError, }; }; diff --git a/plugins/main/public/components/endpoints-summary/index.tsx b/plugins/main/public/components/endpoints-summary/index.tsx index 7b52d47946..0a54fb9ca7 100644 --- a/plugins/main/public/components/endpoints-summary/index.tsx +++ b/plugins/main/public/components/endpoints-summary/index.tsx @@ -56,7 +56,7 @@ export const MainEndpointsSummary = compose( return ( No agents were added to this manager.} + title={

No agents were added to the manager

} body={

Add agents to fleet to start monitoring

} actions={ > => - await paginatedAgentsGroupService({ addOrRemove: 'add', ...parameters }); + await paginatedAgentsRequestService({ + method: 'PUT', + url: '/agents/group', + ...parameters, + }); diff --git a/plugins/main/public/components/endpoints-summary/services/index.tsx b/plugins/main/public/components/endpoints-summary/services/index.tsx index 17910f7e44..11f86cff42 100644 --- a/plugins/main/public/components/endpoints-summary/services/index.tsx +++ b/plugins/main/public/components/endpoints-summary/services/index.tsx @@ -4,6 +4,7 @@ export { removeAgentsFromGroupService } from './remove-agents-from-group'; export { addAgentToGroupService } from './add-agent-to-group'; export { addAgentsToGroupService } from './add-agents-to-group'; export { getGroupsService } from './get-groups'; +export { upgradeAgentService } from './upgrade-agent'; export { upgradeAgentsService } from './upgrade-agents'; export { getOutdatedAgents } from './get-outdated-agents'; export { getTasks } from './get-tasks'; diff --git a/plugins/main/public/components/endpoints-summary/services/paginated-agents-group.test.tsx b/plugins/main/public/components/endpoints-summary/services/paginated-agents-request.test.tsx similarity index 93% rename from plugins/main/public/components/endpoints-summary/services/paginated-agents-group.test.tsx rename to plugins/main/public/components/endpoints-summary/services/paginated-agents-request.test.tsx index e6f506dfe0..a746eb6aa3 100644 --- a/plugins/main/public/components/endpoints-summary/services/paginated-agents-group.test.tsx +++ b/plugins/main/public/components/endpoints-summary/services/paginated-agents-request.test.tsx @@ -1,4 +1,4 @@ -import { paginatedAgentsGroupService } from './paginated-agents-group'; +import { paginatedAgentsRequestService } from './paginated-agents-request'; import { WzRequest } from '../../../react-services/wz-request'; jest.mock('../../../react-services/wz-request', () => ({ @@ -7,7 +7,7 @@ jest.mock('../../../react-services/wz-request', () => ({ }, })); -describe('paginatedAgentsGroupService', () => { +describe('paginatedAgentsRequestService', () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -46,13 +46,14 @@ describe('paginatedAgentsGroupService', () => { ); const params = { - addOrRemove: 'add' as any, + method: 'PUT' as any, + url: '/agents/group', agentIds: ['agent1', 'agent2', 'agent3'], groupId: 'group1', pageSize: 2, }; - const result = await paginatedAgentsGroupService(params); + const result = await paginatedAgentsRequestService(params); expect(WzRequest.apiReq).toHaveBeenCalledWith( 'PUT', @@ -146,13 +147,14 @@ describe('paginatedAgentsGroupService', () => { ); const params = { - addOrRemove: 'add' as any, + method: 'PUT' as any, + url: '/agents/group', agentIds: ['agent1', 'agent2', 'agent3'], groupId: 'group1', pageSize: 2, }; - const result = await paginatedAgentsGroupService(params); + const result = await paginatedAgentsRequestService(params); expect(WzRequest.apiReq).toHaveBeenCalledWith( 'PUT', diff --git a/plugins/main/public/components/endpoints-summary/services/paginated-agents-group.tsx b/plugins/main/public/components/endpoints-summary/services/paginated-agents-request.tsx similarity index 91% rename from plugins/main/public/components/endpoints-summary/services/paginated-agents-group.tsx rename to plugins/main/public/components/endpoints-summary/services/paginated-agents-request.tsx index 512098817c..734a2afece 100644 --- a/plugins/main/public/components/endpoints-summary/services/paginated-agents-group.tsx +++ b/plugins/main/public/components/endpoints-summary/services/paginated-agents-request.tsx @@ -10,17 +10,19 @@ export type ErrorAgent = { id: string[]; }; -export const paginatedAgentsGroupService = async ({ - addOrRemove, +export const paginatedAgentsRequestService = async ({ + method, + url, agentIds, groupId, pageSize = 1000, }: { - addOrRemove: 'add' | 'remove'; + method: 'PUT' | 'DELETE'; + url: string; agentIds: string[]; - groupId: string; + groupId?: string; pageSize?: number; -}): Promise> => { +}): Promise> => { let offset = 0; let requestAgentIds: string[] = []; let allAffectedItems: string[] = []; @@ -45,11 +47,11 @@ export const paginatedAgentsGroupService = async ({ message: responseMessage, }, } = (await WzRequest.apiReq( - addOrRemove === 'add' ? 'PUT' : 'DELETE', - `/agents/group`, + method, + url, { params: { - group_id: groupId, + ...(groupId ? { group_id: groupId } : {}), agents_list: requestAgentIds.join(','), wait_for_complete: true, }, diff --git a/plugins/main/public/components/endpoints-summary/services/remove-agents-from-group.tsx b/plugins/main/public/components/endpoints-summary/services/remove-agents-from-group.tsx index a2000e9ba4..5509698e7d 100644 --- a/plugins/main/public/components/endpoints-summary/services/remove-agents-from-group.tsx +++ b/plugins/main/public/components/endpoints-summary/services/remove-agents-from-group.tsx @@ -1,9 +1,13 @@ import IApiResponse from '../../../react-services/interfaces/api-response.interface'; -import { paginatedAgentsGroupService } from './paginated-agents-group'; +import { paginatedAgentsRequestService } from './paginated-agents-request'; export const removeAgentsFromGroupService = async (parameters: { agentIds: string[]; groupId: string; pageSize?: number; }): Promise> => - await paginatedAgentsGroupService({ addOrRemove: 'remove', ...parameters }); + await paginatedAgentsRequestService({ + method: 'DELETE', + url: '/agents/group', + ...parameters, + }); diff --git a/plugins/main/public/components/endpoints-summary/services/upgrade-agent.tsx b/plugins/main/public/components/endpoints-summary/services/upgrade-agent.tsx new file mode 100644 index 0000000000..6eb0de5d3b --- /dev/null +++ b/plugins/main/public/components/endpoints-summary/services/upgrade-agent.tsx @@ -0,0 +1,11 @@ +import IApiResponse from '../../../react-services/interfaces/api-response.interface'; +import { WzRequest } from '../../../react-services/wz-request'; +import { ResponseUpgradeAgents } from '../types'; + +export const upgradeAgentService = async (agentId: string) => + (await WzRequest.apiReq('PUT', '/agents/upgrade', { + params: { + agents_list: agentId, + wait_for_complete: true, + }, + })) as IApiResponse; diff --git a/plugins/main/public/components/endpoints-summary/services/upgrade-agents.tsx b/plugins/main/public/components/endpoints-summary/services/upgrade-agents.tsx index ea9c8f7e6b..2fc0a1c807 100644 --- a/plugins/main/public/components/endpoints-summary/services/upgrade-agents.tsx +++ b/plugins/main/public/components/endpoints-summary/services/upgrade-agents.tsx @@ -1,15 +1,14 @@ import IApiResponse from '../../../react-services/interfaces/api-response.interface'; -import { WzRequest } from '../../../react-services/wz-request'; import { ResponseUpgradeAgents } from '../types'; +import { paginatedAgentsRequestService } from './paginated-agents-request'; export const upgradeAgentsService = async ({ agentIds, }: { agentIds: string[]; }) => - (await WzRequest.apiReq('PUT', `/agents/upgrade`, { - params: { - agents_list: agentIds.join(','), - wait_for_complete: true, - }, + (await paginatedAgentsRequestService({ + method: 'PUT', + url: '/agents/upgrade', + agentIds, })) as IApiResponse; diff --git a/plugins/main/public/components/endpoints-summary/table/__snapshots__/agents-table.test.tsx.snap b/plugins/main/public/components/endpoints-summary/table/__snapshots__/agents-table.test.tsx.snap index 6cd96fd8bc..5f7489b92b 100644 --- a/plugins/main/public/components/endpoints-summary/table/__snapshots__/agents-table.test.tsx.snap +++ b/plugins/main/public/components/endpoints-summary/table/__snapshots__/agents-table.test.tsx.snap @@ -79,47 +79,6 @@ exports[`AgentsTable component Renders correctly to match the snapshot 1`] = ` -
-
-
- -
-
-
@@ -185,6 +144,47 @@ exports[`AgentsTable component Renders correctly to match the snapshot 1`] = `
+
+
+
+ +
+
+
@@ -735,48 +735,6 @@ exports[`AgentsTable component Renders correctly to match the snapshot with cust
-
-
-
- -
-
-
@@ -842,6 +800,48 @@ exports[`AgentsTable component Renders correctly to match the snapshot with cust
+
+
+
+ +
+
+
@@ -1349,48 +1349,6 @@ exports[`AgentsTable component Renders correctly to match the snapshot with no p
-
-
-
- -
-
-
@@ -1456,6 +1414,48 @@ exports[`AgentsTable component Renders correctly to match the snapshot with no p
+
+
+
+ +
+
+
diff --git a/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx b/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx index 1a799c5bef..0df704c765 100644 --- a/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx +++ b/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx @@ -8,6 +8,7 @@ import { Agent } from '../../types'; export const agentsTableActions = ( allowEditGroups: boolean, + allowUpgrade: boolean, setAgent: (agent: Agent) => void, setIsEditGroupsVisible: (visible: boolean) => void, setIsUpgradeModalVisible: (visible: boolean) => void, @@ -124,7 +125,11 @@ export const agentsTableActions = ( const isOutdated = !!outdatedAgents.find( outdatedAgent => outdatedAgent.id === agent.id, ); - return agent.status === API_NAME_AGENT_STATUS.ACTIVE && isOutdated; + return ( + allowUpgrade && + agent.status === API_NAME_AGENT_STATUS.ACTIVE && + isOutdated + ); }, }, ]; diff --git a/plugins/main/public/components/endpoints-summary/table/actions/edit-groups-modal.tsx b/plugins/main/public/components/endpoints-summary/table/actions/edit-groups-modal.tsx index e2077a4d97..49a5a7bbd6 100644 --- a/plugins/main/public/components/endpoints-summary/table/actions/edit-groups-modal.tsx +++ b/plugins/main/public/components/endpoints-summary/table/actions/edit-groups-modal.tsx @@ -130,7 +130,6 @@ export const EditAgentGroupsModal = compose( diff --git a/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.test.tsx b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.test.tsx index 76ce5111ed..dff25feca7 100644 --- a/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.test.tsx +++ b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.test.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { render, fireEvent, waitFor, act } from '@testing-library/react'; +import { render, fireEvent, act } from '@testing-library/react'; import '@testing-library/jest-dom'; import { UpgradeAgentModal } from './upgrade-agent-modal'; jest.mock('../../services', () => ({ - upgradeAgentsService: jest.fn(), + upgradeAgentService: jest.fn(), })); jest.mock('../../../../react-services/common-services', () => ({ @@ -24,6 +24,7 @@ describe('UpgradeAgentModal component', () => { }} onClose={() => {}} reloadAgents={() => {}} + setIsUpgradePanelClosed={() => {}} />, ); @@ -40,7 +41,7 @@ describe('UpgradeAgentModal component', () => { }); test('should send to upgrade', async () => { - const { getByText, getByRole } = render( + const { getByRole } = render( { }} onClose={() => {}} reloadAgents={() => {}} + setIsUpgradePanelClosed={() => {}} />, ); diff --git a/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.tsx b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.tsx index 0481854917..1b217dcbcf 100644 --- a/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.tsx +++ b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.tsx @@ -5,7 +5,7 @@ import { withErrorBoundary, withReduxProvider } from '../../../common/hocs'; import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../react-services/common-services'; -import { upgradeAgentsService } from '../../services'; +import { upgradeAgentService } from '../../services'; import { Agent } from '../../types'; import { getToasts } from '../../../../kibana-services'; @@ -13,67 +13,75 @@ interface UpgradeAgentModalProps { agent: Agent; onClose: () => void; reloadAgents: () => void; + setIsUpgradePanelClosed: (isUpgradePanelClosed: boolean) => void; } export const UpgradeAgentModal = compose( withErrorBoundary, withReduxProvider, -)(({ agent, onClose, reloadAgents }: UpgradeAgentModalProps) => { - const [isLoading, setIsLoading] = useState(false); - const showToast = ( - color: string, - title: string = '', - text: string = '', - time: number = 3000, - ) => { - getToasts().add({ - color: color, - title: title, - text: text, - toastLifeTimeMs: time, - }); - }; +)( + ({ + agent, + onClose, + reloadAgents, + setIsUpgradePanelClosed, + }: UpgradeAgentModalProps) => { + const [isLoading, setIsLoading] = useState(false); + const showToast = ( + color: string, + title: string = '', + text: string = '', + time: number = 3000, + ) => { + getToasts().add({ + color: color, + title: title, + text: text, + toastLifeTimeMs: time, + }); + }; - const handleOnSave = async () => { - setIsLoading(true); + const handleOnSave = async () => { + setIsLoading(true); - try { - await upgradeAgentsService({ agentIds: [agent.id] }); - showToast('success', 'Upgrade agent', 'Upgrade task in progress'); - reloadAgents(); - } catch (error) { - const options = { - context: `UpgradeAgentModal.handleOnSave`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - store: true, - error: { - error, - message: error.message || error, - title: `Could not upgrade agent`, - }, - }; - getErrorOrchestrator().handleError(options); - } finally { - setIsLoading(false); - onClose(); - } - }; + try { + await upgradeAgentService(agent.id); + showToast('success', 'Upgrade agent', 'Upgrade task in progress'); + reloadAgents(); + setIsUpgradePanelClosed(false); + } catch (error) { + const options = { + context: `UpgradeAgentModal.handleOnSave`, + level: UI_LOGGER_LEVELS.ERROR, + severity: UI_ERROR_SEVERITIES.BUSINESS, + store: true, + error: { + error, + message: error.message || error, + title: `Could not upgrade agent`, + }, + }; + getErrorOrchestrator().handleError(options); + } finally { + onClose(); + } + }; - return ( - { - ev.stopPropagation(); - }} - isLoading={isLoading} - > -

{`Upgrade agent ${agent?.name}?`}

-
- ); -}); + return ( + { + ev.stopPropagation(); + }} + isLoading={isLoading} + > +

{`Upgrade agent ${agent?.name}?`}

+
+ ); + }, +); diff --git a/plugins/main/public/components/endpoints-summary/table/agents-table.tsx b/plugins/main/public/components/endpoints-summary/table/agents-table.tsx index 640e24edd1..e84419283a 100644 --- a/plugins/main/public/components/endpoints-summary/table/agents-table.tsx +++ b/plugins/main/public/components/endpoints-summary/table/agents-table.tsx @@ -45,6 +45,7 @@ import { getOutdatedAgents } from '../services'; import { UI_ERROR_SEVERITIES } from '../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../react-services/common-services'; import { AgentUpgradesInProgress } from './upgrades-in-progress/upgrades-in-progress'; +import { AgentUpgradesTaskDetailsModal } from './upgrade-task-details-modal'; const searchBarWQLOptions = { implicitQuery: { @@ -93,7 +94,17 @@ export const AgentsTable = compose( const [denyEditGroups] = useUserPermissionsRequirements([ { action: 'group:modify_assignments', resource: 'group:id:*' }, ]); + const [denyUpgrade] = useUserPermissionsRequirements([ + { action: 'agent:upgrade', resource: 'agent:id:*' }, + ]); + const [denyGetTasks] = useUserPermissionsRequirements([ + { action: 'task:status', resource: '*:*:*' }, + ]); + const [outdatedAgents, setOutdatedAgents] = useState([]); + const [isUpgradeTasksModalVisible, setIsUpgradeTasksModalVisible] = + useState(false); + const [isUpgradePanelClosed, setIsUpgradePanelClosed] = useState(false); useEffect(() => { if (sessionStorage.getItem('wz-agents-overview-table-filter')) { @@ -230,41 +241,49 @@ export const AgentsTable = compose( } + extra={ + + } actionButtons={({ filters }) => ( - <> - - - Deploy new agent - - - - reloadAgents()} - /> - - + + + Deploy new agent + + + )} + postActionButtons={({ filters }) => ( + + reloadAgents()} + setIsUpgradeTasksModalVisible={setIsUpgradeTasksModalVisible} + setIsUpgradePanelClosed={setIsUpgradePanelClosed} + /> + )} endpoint='/agents' tableColumns={agentsTableColumns( !denyEditGroups, + !denyUpgrade, setAgent, setIsEditGroupsVisible, setIsUpgradeModalVisible, @@ -464,6 +483,12 @@ export const AgentsTable = compose( setIsEditGroupsVisible(false); setAgent(undefined); }} + setIsUpgradePanelClosed={setIsUpgradePanelClosed} + /> + ) : null} + {isUpgradeTasksModalVisible ? ( + setIsUpgradeTasksModalVisible(false)} /> ) : null}
diff --git a/plugins/main/public/components/endpoints-summary/table/columns.tsx b/plugins/main/public/components/endpoints-summary/table/columns.tsx index c1f1b433ba..e67361d37d 100644 --- a/plugins/main/public/components/endpoints-summary/table/columns.tsx +++ b/plugins/main/public/components/endpoints-summary/table/columns.tsx @@ -17,6 +17,7 @@ import { Agent } from '../types'; // This is added to prevent the wrap because of the table-layout: auto export const agentsTableColumns = ( allowEditGroups: boolean, + allowUpgrade: boolean, setAgent: (agents: Agent) => void, setIsEditGroupsVisible: (visible: boolean) => void, setIsUpgradeModalVisible: (visible: boolean) => void, @@ -156,6 +157,7 @@ export const agentsTableColumns = ( show: true, actions: agentsTableActions( allowEditGroups, + allowUpgrade, setAgent, setIsEditGroupsVisible, setIsUpgradeModalVisible, diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/__snapshots__/global-actions.test.tsx.snap b/plugins/main/public/components/endpoints-summary/table/global-actions/__snapshots__/global-actions.test.tsx.snap index b67113993a..1e2d0d4915 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/__snapshots__/global-actions.test.tsx.snap +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/__snapshots__/global-actions.test.tsx.snap @@ -33,7 +33,7 @@ exports[`AgentsTableGlobalActions component should return the component 1`] = ` - Add/Remove groups + More diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx b/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx index b01644ae66..4058e70462 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/edit-groups/result.tsx @@ -12,7 +12,8 @@ import { EuiSpacer, } from '@elastic/eui'; import { Agent } from '../../../types'; -import { ErrorAgent, GroupResult, RESULT_TYPE } from './edit-groups-modal'; +import { GroupResult, RESULT_TYPE } from './edit-groups-modal'; +import { ErrorAgent } from '../../../services/paginated-agents-group'; interface EditAgentsGroupsModalResultProps { addOrRemove: 'add' | 'remove'; @@ -40,7 +41,7 @@ export const EditAgentsGroupsModalResult = ({ columns={[ { field: 'id', - name: 'ID', + name: 'Id', align: 'left', sortable: true, }, @@ -71,6 +72,7 @@ export const EditAgentsGroupsModalResult = ({ name: 'Code', align: 'left', sortable: true, + width: '100px', }, { field: 'error.message', @@ -184,7 +186,6 @@ export const EditAgentsGroupsModalResult = ({ result, successAgents, errorAgents, - errorMessage, totalErrorAgents, } = groupResult; diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.test.tsx b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.test.tsx index daaf7745a8..3b09b24d68 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.test.tsx +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.test.tsx @@ -18,12 +18,16 @@ describe('AgentsTableGlobalActions component', () => { filters={{}} allowEditGroups={true} reloadAgents={() => {}} + setIsUpgradeTasksModalVisible={() => {}} + setIsUpgradePanelClosed={() => {}} + allowUpgrade={true} + allowGetTasks={true} />, ); expect(container).toMatchSnapshot(); - const option = getByText('Add/Remove groups'); + const option = getByText('More'); expect(option).toBeInTheDocument(); }); @@ -36,10 +40,14 @@ describe('AgentsTableGlobalActions component', () => { filters={{}} allowEditGroups={true} reloadAgents={() => {}} + setIsUpgradeTasksModalVisible={() => {}} + setIsUpgradePanelClosed={() => {}} + allowUpgrade={true} + allowGetTasks={true} />, ); - const option = getByText('Add/Remove groups'); + const option = getByText('More'); expect(option).toBeInTheDocument(); act(() => { diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx index 236569fbc4..78f4078758 100644 --- a/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/global-actions.tsx @@ -4,10 +4,12 @@ import { EuiButtonEmpty, EuiContextMenuPanel, EuiContextMenuItem, + EuiHorizontalRule, } from '@elastic/eui'; import { WzElementPermissions } from '../../../common/permissions/element'; import { Agent } from '../../types'; import { EditAgentsGroupsModal } from './edit-groups/edit-groups-modal'; +import { UpgradeAgentsModal } from './upgrade/upgrade-modal'; export interface AgentsTableGlobalActionsProps { selectedAgents: Agent[]; @@ -15,7 +17,11 @@ export interface AgentsTableGlobalActionsProps { allAgentsCount: number; filters: any; allowEditGroups: boolean; + allowUpgrade: boolean; + allowGetTasks: boolean; reloadAgents: () => void; + setIsUpgradeTasksModalVisible: (isModalVisible: boolean) => void; + setIsUpgradePanelClosed: (isUpgradePanelClosed: boolean) => void; } export const AgentsTableGlobalActions = ({ @@ -24,13 +30,18 @@ export const AgentsTableGlobalActions = ({ allAgentsCount, filters, allowEditGroups, + allowUpgrade, + allowGetTasks, reloadAgents, + setIsUpgradeTasksModalVisible, + setIsUpgradePanelClosed, }: AgentsTableGlobalActionsProps) => { const [isPopoverOpen, setPopover] = useState(false); const [isEditGroupsVisible, setIsEditGroupsVisible] = useState(false); const [addOrRemoveGroups, setAddOrRemoveGroups] = useState< 'add' | 'remove' >(); + const [isUpgradeAgentsVisible, setIsUpgradeAgentsVisible] = useState(false); const onButtonClick = () => { setPopover(!isPopoverOpen); @@ -46,7 +57,7 @@ export const AgentsTableGlobalActions = ({ iconSide='right' onClick={onButtonClick} > - Add/Remove groups + More ); @@ -112,6 +123,48 @@ export const AgentsTableGlobalActions = ({ + + { + closePopover(); + setIsUpgradeAgentsVisible(true); + }} + > + + + Upgrade agents + {totalAgents ? ` (${totalAgents})` : ''} + + + + { + closePopover(); + setIsUpgradeTasksModalVisible(true); + }} + > + + Upgrade task details + + {isEditGroupsVisible ? ( @@ -126,6 +179,18 @@ export const AgentsTableGlobalActions = ({ addOrRemove={addOrRemoveGroups} /> ) : null} + {isUpgradeAgentsVisible ? ( + reloadAgents()} + onClose={() => { + setIsUpgradeAgentsVisible(false); + }} + setIsUpgradePanelClosed={setIsUpgradePanelClosed} + /> + ) : null} ); }; diff --git a/plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/__snapshots__/upgrade-modal.test.tsx.snap b/plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/__snapshots__/upgrade-modal.test.tsx.snap new file mode 100644 index 0000000000..4cf80094a5 --- /dev/null +++ b/plugins/main/public/components/endpoints-summary/table/global-actions/upgrade/__snapshots__/upgrade-modal.test.tsx.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UpgradeAgentsModal component should return the component 1`] = ` + @@ -96,7 +124,7 @@ exports[`AgentUpgradesInProgress component should return the component 1`] = ` 2 - Failed upgrade tasks + Failed diff --git a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/taskDetailsButton.tsx b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/taskDetailsButton.tsx deleted file mode 100644 index 5e1753dde7..0000000000 --- a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/taskDetailsButton.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import React, { useState, memo } from 'react'; -import { EuiHealth, EuiIconTip } from '@elastic/eui'; -import { TableWzAPI } from '../../../common/tables'; -import { formatUIDate } from '../../../../react-services/time-service'; -import { - API_NAME_TASK_STATUS, - SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT, - UI_TASK_STATUS, -} from '../../../../../common/constants'; -import { WzRequest } from '../../../../react-services/wz-request'; -import { get as getLodash, uniqBy as uniqByLodash } from 'lodash'; -import { - EuiModal, - EuiModalHeader, - EuiModalBody, - EuiModalFooter, - EuiButton, - EuiButtonEmpty, -} from '@elastic/eui'; - -export const AgentUpgradesTaskDetailsButton = memo(() => { - const [isModalVisible, setIsModalVisible] = useState(false); - - const datetime = new Date(); - datetime.setMinutes(datetime.getMinutes() - 60); - const formattedDate = datetime.toISOString(); - - const defaultFilters = { - q: `last_update_time>${formattedDate}`, - }; - - const handleOnCloseModal = () => setIsModalVisible(false); - - return ( - <> - setIsModalVisible(true)} - iconType='eye' - > - Task details - - {isModalVisible ? ( - - - - - Create{' '} - - - ), - sortable: true, - searchable: false, - show: true, - render: value => formatUIDate(value), - }, - { - field: 'last_update_time', - name: ( - - Last update{' '} - - - ), - sortable: true, - searchable: false, - show: true, - render: value => formatUIDate(value), - }, - { - field: 'status', - name: 'Status', - width: '100px', - sortable: true, - searchable: true, - show: true, - render: value => ( - - {value} - - ), - }, - { - field: 'error_message', - name: 'Error', - show: true, - searchable: true, - }, - ]} - tableInitialSortingField='last_update_time' - tableInitialSortingDirection='desc' - tablePageSizeOptions={[10, 25, 50, 100]} - filters={defaultFilters} - downloadCsv - showReload - showFieldSelector - searchTable - searchBarWQL={{ - suggestions: { - field(currentValue) { - return [ - { - label: 'agent_id', - description: 'filter by agent id', - }, - { label: 'status', description: 'filter by status' }, - { - label: 'create_time', - description: 'filter by creation date', - }, - { - label: 'last_update_time', - description: 'filter by last update date', - }, - { label: 'task_id', description: 'filter by task id' }, - ]; - }, - value: async (currentValue, { field }) => { - try { - switch (field) { - case 'status': - return UI_TASK_STATUS.map(status => ({ - label: status, - })); - default: { - const response = await WzRequest.apiReq( - 'GET', - '/tasks/status', - { - params: { - limit: SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT, - select: field, - sort: `+${field}`, - ...(currentValue - ? { - q: `${field}~${currentValue}`, - } - : {}), - }, - }, - ); - const suggestionValues = - response?.data?.data.affected_items.map(item => ({ - label: getLodash(item, field), - })); - return uniqByLodash(suggestionValues, 'label'); - } - } - } catch (error) { - return []; - } - }, - }, - validate: { - value: ({ formattedValue, value: rawValue }, { field }) => { - const value = formattedValue ?? rawValue; - if (value) { - if (['create_time', 'last_update_time'].includes(field)) { - const isCorrectDate = - /^\d{4}-\d{2}-\d{2}([ T]\d{2}:\d{2}:\d{2}(.\d{1,6})?Z?)?$/.test( - value, - ); - return isCorrectDate - ? undefined - : `"${value}" is not a expected format. Valid formats: YYYY-MM-DD, YYYY-MM-DD HH:mm:ss, YYYY-MM-DDTHH:mm:ss, YYYY-MM-DDTHH:mm:ssZ.`; - } - } - }, - }, - }} - tableProps={{ - tableLayout: 'auto', - }} - /> - - - - Close - - - - ) : null} - - ); -}); diff --git a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.test.tsx b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.test.tsx index df4b3d67eb..d9d82e215d 100644 --- a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.test.tsx +++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.test.tsx @@ -24,34 +24,44 @@ describe('AgentUpgradesInProgress component', () => { }); const { container, getByText } = render( - , + {}} + isPanelClosed={false} + setIsPanelClosed={() => {}} + allowGetTasks={true} + />, ); expect(container).toMatchSnapshot(); const inProgressValue = getByText('5'); expect(inProgressValue).toBeInTheDocument(); - const inProgressText = getByText('Upgrade tasks in progress'); + const inProgressText = getByText('In progress'); expect(inProgressText).toBeInTheDocument(); const failedValue = getByText('2'); expect(failedValue).toBeInTheDocument(); - const failedText = getByText('Failed upgrade tasks'); + const failedText = getByText('Failed'); expect(failedText).toBeInTheDocument(); }); test('should show upgrade tasks modal', async () => { - const { getByRole, getByText } = render( - , + const { getByRole } = render( + {}} + isPanelClosed={false} + setIsPanelClosed={() => {}} + allowGetTasks={true} + />, ); - const openModalButton = getByRole('button'); + const openModalButton = getByRole('button', { name: 'Task details' }); expect(openModalButton).toBeInTheDocument(); act(() => { fireEvent.click(openModalButton); }); - - await waitFor(() => expect(getByRole('table')).toBeInTheDocument()); }); }); diff --git a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.tsx b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.tsx index 81b1133c94..af02927db6 100644 --- a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.tsx +++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.tsx @@ -7,29 +7,44 @@ import { EuiFlexItem, EuiIconTip, EuiSpacer, + EuiButtonEmpty, + EuiButtonIcon, } from '@elastic/eui'; import { useGetUpgradeTasks } from '../../hooks'; -import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; +import { + API_NAME_TASK_STATUS, + UI_LOGGER_LEVELS, +} from '../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../react-services/common-services'; -import { AgentUpgradesTaskDetailsButton } from './taskDetailsButton'; interface AgentUpgradesInProgress { reload: any; + setIsModalVisible: (isModalVisible: boolean) => void; + isPanelClosed: boolean; + setIsPanelClosed: (isPanelClosed: boolean) => void; + allowGetTasks: boolean; } export const AgentUpgradesInProgress = ({ reload, + setIsModalVisible, + isPanelClosed, + setIsPanelClosed, + allowGetTasks, }: AgentUpgradesInProgress) => { const [isUpgrading, setIsUpgrading] = useState(false); + const { - totalInProgressTasks, - getInProgressError, - totalSuccessTasks, - getSuccessError, - totalErrorUpgradeTasks, - getErrorTasksError, - } = useGetUpgradeTasks(reload); + totalInProgressTasks = 0, + getInProgressError = undefined, + totalSuccessTasks = 0, + getSuccessError = undefined, + totalErrorUpgradeTasks = 0, + getErrorTasksError = undefined, + totalTimeoutUpgradeTasks = 0, + getTimeoutError = undefined, + } = allowGetTasks ? useGetUpgradeTasks(reload) : {}; useEffect(() => { if (totalInProgressTasks > 0) { @@ -37,59 +52,75 @@ export const AgentUpgradesInProgress = ({ } }, [totalInProgressTasks]); - if (getInProgressError) { + const showErrorToast = (status: string, error: any) => { + API_NAME_TASK_STATUS; const options = { context: `AgentUpgradesInProgress.useGetUpgradeTasks`, level: UI_LOGGER_LEVELS.ERROR, severity: UI_ERROR_SEVERITIES.BUSINESS, store: true, error: { - error: getInProgressError, - message: getInProgressError.message || getInProgressError, - title: `Could not get upgrade progress tasks`, + error, + message: error.message || error, + title: `Could not get upgrade tasks: ${status}`, }, }; getErrorOrchestrator().handleError(options); + }; + + if (getInProgressError) { + showErrorToast(API_NAME_TASK_STATUS.IN_PROGRESS, getInProgressError); } if (getSuccessError) { - const options = { - context: `AgentUpgradesInProgress.useGetUpgradeTasks`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - store: true, - error: { - error: getSuccessError, - message: getSuccessError.message || getSuccessError, - title: `Could not get upgrade success tasks`, - }, - }; - getErrorOrchestrator().handleError(options); + showErrorToast(API_NAME_TASK_STATUS.DONE, getSuccessError); } if (getErrorTasksError) { - const options = { - context: `AgentUpgradesInProgress.useGetUpgradeTasks`, - level: UI_LOGGER_LEVELS.ERROR, - severity: UI_ERROR_SEVERITIES.BUSINESS, - store: true, - error: { - error: getErrorTasksError, - message: getErrorTasksError.message || getErrorTasksError, - title: `Could not get upgrade error tasks`, - }, - }; - getErrorOrchestrator().handleError(options); + showErrorToast(API_NAME_TASK_STATUS.FAILED, getErrorTasksError); + } + + if (getTimeoutError) { + showErrorToast(API_NAME_TASK_STATUS.TIMEOUT, getTimeoutError); } - return isUpgrading || totalSuccessTasks || totalErrorUpgradeTasks ? ( + const showTasks = isUpgrading || totalSuccessTasks || totalErrorUpgradeTasks; + if (isPanelClosed || !showTasks) return null; + + return ( - - - Upgrade tasks - + + + + Upgrade tasks + + + setIsModalVisible(true)} + iconType='eye' + > + Task details + + + - + setIsPanelClosed(true)} + color='text' + iconType='cross' + aria-label='Close' + /> @@ -100,9 +131,7 @@ export const AgentUpgradesInProgress = ({ {totalInProgressTasks} - {totalInProgressTasks === 1 - ? ' Upgrade task in progress' - : ' Upgrade tasks in progress'} + {` ${API_NAME_TASK_STATUS.IN_PROGRESS}`}
@@ -120,9 +149,7 @@ export const AgentUpgradesInProgress = ({ {totalSuccessTasks} - {totalSuccessTasks === 1 - ? ' Success upgrade task ' - : ' Success upgrade tasks '} + {` ${API_NAME_TASK_STATUS.DONE} `} @@ -142,10 +169,27 @@ export const AgentUpgradesInProgress = ({ {totalErrorUpgradeTasks} - {totalErrorUpgradeTasks === 1 - ? ' Failed upgrade task ' - : ' Failed upgrade tasks '} - + {` ${API_NAME_TASK_STATUS.FAILED} `} + + + + +
+ ) : null} + {totalTimeoutUpgradeTasks > 0 ? ( + + + + + + {totalTimeoutUpgradeTasks} + {` ${API_NAME_TASK_STATUS.TIMEOUT} `} @@ -154,5 +198,5 @@ export const AgentUpgradesInProgress = ({ ) : null} - ) : null; + ); }; diff --git a/plugins/main/public/types.ts b/plugins/main/public/types.ts index 647791058f..9d3c0e7915 100644 --- a/plugins/main/public/types.ts +++ b/plugins/main/public/types.ts @@ -19,7 +19,6 @@ import { } from '../../../src/plugins/telemetry/public'; import { WazuhCheckUpdatesPluginStart } from '../../wazuh-check-updates/public'; import { WazuhCorePluginStart } from '../../wazuh-core/public'; -import { WazuhEndpointsPluginStart } from '../../wazuh-endpoints/public'; import { DashboardStart } from '../../../src/plugins/dashboard/public'; export interface AppPluginStartDependencies { @@ -33,7 +32,6 @@ export interface AppPluginStartDependencies { telemetry: TelemetryPluginStart; wazuhCheckUpdates: WazuhCheckUpdatesPluginStart; wazuhCore: WazuhCorePluginStart; - wazuhEndpoints: WazuhEndpointsPluginStart; dashboard: DashboardStart; } export interface AppDependencies { diff --git a/plugins/wazuh-endpoints/.i18nrc.json b/plugins/wazuh-endpoints/.i18nrc.json deleted file mode 100644 index 57c921787c..0000000000 --- a/plugins/wazuh-endpoints/.i18nrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "prefix": "wazuhEndpoints", - "paths": { - "wazuhEndpoints": "." - }, - "translations": ["translations/en-US.json"] -} diff --git a/plugins/wazuh-endpoints/common/constants.ts b/plugins/wazuh-endpoints/common/constants.ts deleted file mode 100644 index ab242f7d67..0000000000 --- a/plugins/wazuh-endpoints/common/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const PLUGIN_ID = 'wazuhEndpoints'; -export const PLUGIN_NAME = 'wazuh_endpoints'; - -export enum routes {} diff --git a/plugins/wazuh-endpoints/common/types.ts b/plugins/wazuh-endpoints/common/types.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/wazuh-endpoints/opensearch_dashboards.json b/plugins/wazuh-endpoints/opensearch_dashboards.json deleted file mode 100644 index 37806a8aa4..0000000000 --- a/plugins/wazuh-endpoints/opensearch_dashboards.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "wazuhEndpoints", - "version": "4.9.0-00", - "opensearchDashboardsVersion": "opensearchDashboards", - "server": true, - "ui": true, - "requiredPlugins": ["navigation"], - "optionalPlugins": [] -} diff --git a/plugins/wazuh-endpoints/package.json b/plugins/wazuh-endpoints/package.json deleted file mode 100644 index 6fb24b72b5..0000000000 --- a/plugins/wazuh-endpoints/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "wazuh-endpoints", - "version": "4.9.0", - "revision": "00", - "pluginPlatform": { - "version": "2.12.0" - }, - "description": "Wazuh Endpoints", - "private": true, - "scripts": { - "build": "yarn plugin-helpers build --opensearch-dashboards-version=$OPENSEARCH_DASHBOARDS_VERSION", - "plugin-helpers": "node ../../scripts/plugin_helpers", - "osd": "node ../../scripts/osd", - "test:ui:runner": "node ../../scripts/functional_test_runner.js", - "test:server": "plugin-helpers test:server", - "test:browser": "plugin-helpers test:browser", - "test:jest": "node scripts/jest --runInBand", - "test:jest:runner": "node scripts/runner test" - } -} diff --git a/plugins/wazuh-endpoints/public/index.ts b/plugins/wazuh-endpoints/public/index.ts deleted file mode 100644 index fee1d9b0fa..0000000000 --- a/plugins/wazuh-endpoints/public/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { WazuhEndpointsPlugin } from './plugin'; - -// This exports static code and TypeScript types, -// as well as, OpenSearch Dashboards Platform `plugin()` initializer. -export function plugin() { - return new WazuhEndpointsPlugin(); -} -export { WazuhEndpointsPluginSetup, WazuhEndpointsPluginStart } from './types'; diff --git a/plugins/wazuh-endpoints/public/plugin.ts b/plugins/wazuh-endpoints/public/plugin.ts deleted file mode 100644 index f2e9e743b3..0000000000 --- a/plugins/wazuh-endpoints/public/plugin.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CoreSetup, CoreStart, Plugin } from 'opensearch-dashboards/public'; -import { - AppPluginStartDependencies, - WazuhEndpointsPluginSetup, - WazuhEndpointsPluginStart, -} from './types'; - -export class WazuhEndpointsPlugin - implements Plugin -{ - public setup(core: CoreSetup): WazuhEndpointsPluginSetup { - return {}; - } - - public start( - core: CoreStart, - plugins: AppPluginStartDependencies, - ): WazuhEndpointsPluginStart { - return {}; - } - - public stop() {} -} diff --git a/plugins/wazuh-endpoints/public/types.ts b/plugins/wazuh-endpoints/public/types.ts deleted file mode 100644 index bb42fe7913..0000000000 --- a/plugins/wazuh-endpoints/public/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface WazuhEndpointsPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WazuhEndpointsPluginStart {} - -export interface AppPluginStartDependencies {} diff --git a/plugins/wazuh-endpoints/scripts/jest.js b/plugins/wazuh-endpoints/scripts/jest.js deleted file mode 100644 index cb58c54ec0..0000000000 --- a/plugins/wazuh-endpoints/scripts/jest.js +++ /dev/null @@ -1,19 +0,0 @@ -// # Run Jest tests -// -// All args will be forwarded directly to Jest, e.g. to watch tests run: -// -// node scripts/jest --watch -// -// or to build code coverage: -// -// node scripts/jest --coverage -// -// See all cli options in https://facebook.github.io/jest/docs/cli.html - -const path = require('path'); -process.argv.push('--config', path.resolve(__dirname, '../test/jest/config.js')); - -require('../../../src/setup_node_env'); -const jest = require('../../../node_modules/jest'); - -jest.run(process.argv.slice(2)); diff --git a/plugins/wazuh-endpoints/scripts/manifest.js b/plugins/wazuh-endpoints/scripts/manifest.js deleted file mode 100644 index 711059d3ac..0000000000 --- a/plugins/wazuh-endpoints/scripts/manifest.js +++ /dev/null @@ -1,17 +0,0 @@ - -/* eslint-disable @typescript-eslint/no-var-requires */ - -const fs = require('fs'); - -/** - * Reads the package.json file. - * @returns {Object} JSON object. - */ -function loadPackageJson() { - const packageJson = fs.readFileSync('./package.json'); - return JSON.parse(packageJson); -} - -module.exports = { - loadPackageJson -}; \ No newline at end of file diff --git a/plugins/wazuh-endpoints/scripts/runner.js b/plugins/wazuh-endpoints/scripts/runner.js deleted file mode 100755 index 5ba9b132ab..0000000000 --- a/plugins/wazuh-endpoints/scripts/runner.js +++ /dev/null @@ -1,148 +0,0 @@ -/* eslint-disable array-element-newline */ -/* eslint-disable @typescript-eslint/no-var-requires */ - -/** -Runs yarn commands using a Docker container. - -Intended to test and build locally. - -Uses development images. Must be executed from the root folder of the project. - -See /docker/runner/docker-compose.yml for available environment variables. - -# Usage: -# ------------- -# - node scripts/runner [] -# - yarn test:jest:runner [] -# - yarn build:runner -*/ - -const childProcess = require('child_process'); -const { loadPackageJson } = require('./manifest'); - -const COMPOSE_DIR = '../../docker/runner'; - -function getProjectInfo() { - const manifest = loadPackageJson(); - - return { - app: 'osd', - version: manifest['pluginPlatform']['version'], - repo: process.cwd(), - }; -} - -function getBuildArgs({ app, version }) { - return `--opensearch-dashboards-version=${version}`; -} - -/** - * Transforms the Jest CLI options from process.argv back to a string. - * If no options are provided, default ones are generated. - * @returns {String} Space separated string with all Jest CLI options provided. - */ -function getJestArgs() { - // Take args only after `test` word - const index = process.argv.indexOf('test'); - const args = process.argv.slice(index + 1); - // Remove duplicates using set - return Array.from(new Set([...args, '--runInBand'])).join(' '); -} - -/** - * Generates the execution parameters if they are not set. - * @returns {Object} Default environment variables. - */ -const buildEnvVars = ({ app, version, repo, cmd, args }) => { - return { - APP: app, - VERSION: version, - REPO: repo, - CMD: cmd, - ARGS: args, - }; -}; - -/** - * Captures the SIGINT signal (Ctrl + C) to stop the container and exit. - */ -function setupAbortController() { - process.on('SIGINT', () => { - childProcess.spawnSync('docker', [ - 'compose', - '--project-directory', - COMPOSE_DIR, - 'stop', - ]); - process.exit(); - }); -} - -/** - * Start the container. - */ -function startRunner() { - const runner = childProcess.spawn('docker', [ - 'compose', - '--project-directory', - COMPOSE_DIR, - 'up', - ]); - - runner.stdout.on('data', data => { - console.log(`${data}`); - }); - - runner.stderr.on('data', data => { - console.error(`${data}`); - }); -} - -/** - * Main function - */ -function main() { - if (process.argv.length < 2) { - process.stderr.write('Required parameters not provided'); - process.exit(-1); - } - - const projectInfo = getProjectInfo(); - let envVars = {}; - - switch (process.argv[2]) { - case 'build': - envVars = buildEnvVars({ - ...projectInfo, - cmd: 'plugin-helpers build', - args: getBuildArgs({ ...projectInfo }), - }); - break; - - case 'test': - envVars = buildEnvVars({ - ...projectInfo, - cmd: 'test:jest', - args: getJestArgs(), - }); - break; - - default: - // usage(); - console.error('Unsupported or invalid yarn command.'); - process.exit(-1); - } - - // Check the required environment variables are set - for (const [key, value] of Object.entries(envVars)) { - if (!process.env[key]) { - process.env[key] = value; - } - console.log(`${key}: ${process.env[key]}`); - } - - setupAbortController(); - startRunner(); -} - -main(); diff --git a/plugins/wazuh-endpoints/server/index.ts b/plugins/wazuh-endpoints/server/index.ts deleted file mode 100644 index ac32d575e6..0000000000 --- a/plugins/wazuh-endpoints/server/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { PluginInitializerContext } from '../../../src/core/server'; -import { WazuhEndpointsPlugin } from './plugin'; - -// This exports static code and TypeScript types, -// as well as, OpenSearch Dashboards Platform `plugin()` initializer. - -export function plugin(initializerContext: PluginInitializerContext) { - return new WazuhEndpointsPlugin(initializerContext); -} - -export { WazuhEndpointsPluginSetup, WazuhEndpointsPluginStart } from './types'; diff --git a/plugins/wazuh-endpoints/server/plugin.ts b/plugins/wazuh-endpoints/server/plugin.ts deleted file mode 100644 index 5d238e9787..0000000000 --- a/plugins/wazuh-endpoints/server/plugin.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - PluginInitializerContext, - CoreSetup, - CoreStart, - Plugin, - Logger, -} from 'opensearch-dashboards/server'; - -import { - PluginSetup, - WazuhEndpointsPluginSetup, - WazuhEndpointsPluginStart, - AppPluginStartDependencies, -} from './types'; - -export class WazuhEndpointsPlugin - implements Plugin { - private readonly logger: Logger; - - constructor(initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get(); - } - - public async setup(core: CoreSetup, plugins: PluginSetup) { - this.logger.debug('wazuh_endpoints: Setup'); - - return {}; - } - - public start(core: CoreStart, plugins: AppPluginStartDependencies): WazuhEndpointsPluginStart { - this.logger.debug('wazuh_endpoints: Started'); - - return {}; - } - - public stop() {} -} diff --git a/plugins/wazuh-endpoints/server/types.ts b/plugins/wazuh-endpoints/server/types.ts deleted file mode 100644 index 773101157d..0000000000 --- a/plugins/wazuh-endpoints/server/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface AppPluginStartDependencies {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WazuhEndpointsPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface WazuhEndpointsPluginStart {} - -export type PluginSetup = {}; - -export interface AppPluginStartDependencies {} diff --git a/plugins/wazuh-endpoints/test/jest/config.js b/plugins/wazuh-endpoints/test/jest/config.js deleted file mode 100644 index c49cd92aa0..0000000000 --- a/plugins/wazuh-endpoints/test/jest/config.js +++ /dev/null @@ -1,41 +0,0 @@ -import path from 'path'; - -const kbnDir = path.resolve(__dirname, '../../../../'); - -export default { - rootDir: path.resolve(__dirname, '../..'), - roots: ['/public', '/server', '/common'], - modulePaths: [`${kbnDir}/node_modules`], - collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', './!**/node_modules/**'], - moduleNameMapper: { - '^ui/(.*)': `${kbnDir}/src/ui/public/$1`, - // eslint-disable-next-line max-len - '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `${kbnDir}/src/dev/jest/mocks/file_mock.js`, - '\\.(css|less|scss)$': `${kbnDir}/src/dev/jest/mocks/style_mock.js`, - axios: 'axios/dist/node/axios.cjs', - }, - setupFiles: [ - `${kbnDir}/src/dev/jest/setup/babel_polyfill.js`, - `${kbnDir}/src/dev/jest/setup/enzyme.js`, - ], - collectCoverage: true, - coverageDirectory: './target/test-coverage', - coverageReporters: ['html', 'text-summary', 'json-summary'], - globals: { - 'ts-jest': { - skipBabel: true, - }, - }, - moduleFileExtensions: ['js', 'json', 'ts', 'tsx', 'html'], - modulePathIgnorePatterns: ['__fixtures__/', 'target/'], - testMatch: ['**/*.test.{js,ts,tsx}'], - transform: { - '^.+\\.js$': `${kbnDir}/src/dev/jest/babel_transform.js`, - '^.+\\.tsx?$': `${kbnDir}/src/dev/jest/babel_transform.js`, - '^.+\\.html?$': `${kbnDir}/src/dev/jest/babel_transform.js`, - }, - transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.js$'], - snapshotSerializers: [`${kbnDir}/node_modules/enzyme-to-json/serializer`], - testEnvironment: 'jest-environment-jsdom', - reporters: ['default', `${kbnDir}/src/dev/jest/junit_reporter.js`], -}; diff --git a/plugins/wazuh-endpoints/translations/en-US.json b/plugins/wazuh-endpoints/translations/en-US.json deleted file mode 100644 index 9022cc65e3..0000000000 --- a/plugins/wazuh-endpoints/translations/en-US.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "formats": { - "number": { - "currency": { - "style": "currency" - }, - "percent": { - "style": "percent" - } - }, - "date": { - "short": { - "month": "numeric", - "day": "numeric", - "year": "2-digit" - }, - "medium": { - "month": "short", - "day": "numeric", - "year": "numeric" - }, - "long": { - "month": "long", - "day": "numeric", - "year": "numeric" - }, - "full": { - "weekday": "long", - "month": "long", - "day": "numeric", - "year": "numeric" - } - }, - "time": { - "short": { - "hour": "numeric", - "minute": "numeric" - }, - "medium": { - "hour": "numeric", - "minute": "numeric", - "second": "numeric" - }, - "long": { - "hour": "numeric", - "minute": "numeric", - "second": "numeric", - "timeZoneName": "short" - }, - "full": { - "hour": "numeric", - "minute": "numeric", - "second": "numeric", - "timeZoneName": "short" - } - }, - "relative": { - "years": { - "units": "year" - }, - "months": { - "units": "month" - }, - "days": { - "units": "day" - }, - "hours": { - "units": "hour" - }, - "minutes": { - "units": "minute" - }, - "seconds": { - "units": "second" - } - } - }, - "messages": {} -} diff --git a/plugins/wazuh-endpoints/tsconfig.json b/plugins/wazuh-endpoints/tsconfig.json deleted file mode 100644 index d3b63f9aee..0000000000 --- a/plugins/wazuh-endpoints/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./target", - "skipLibCheck": true - }, - "include": [ - "index.ts", - "common/**/*.ts", - "public/**/*.ts", - "public/**/*.tsx", - "server/**/*.ts", - "../../typings/**/*", - "public/hooks" - ], - "exclude": [] -} \ No newline at end of file