@@ -562,7 +565,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot 1`] = `
data-test-subj="tableHeaderCell_version_6"
role="columnheader"
scope="col"
- style="width:10%"
+ style="width:100px"
>
+
@@ -1149,7 +1155,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot with cust
data-test-subj="tableHeaderCell_version_3"
role="columnheader"
scope="col"
- style="width:10%"
+ style="width:100px"
>
+
@@ -1832,7 +1841,7 @@ exports[`AgentsTable component Renders correctly to match the snapshot with no p
data-test-subj="tableHeaderCell_version_6"
role="columnheader"
scope="col"
- style="width:10%"
+ style="width:100px"
>
+`;
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 d6137893e8..1a799c5bef 100644
--- a/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx
+++ b/plugins/main/public/components/endpoints-summary/table/actions/actions.tsx
@@ -10,6 +10,8 @@ export const agentsTableActions = (
allowEditGroups: boolean,
setAgent: (agent: Agent) => void,
setIsEditGroupsVisible: (visible: boolean) => void,
+ setIsUpgradeModalVisible: (visible: boolean) => void,
+ outdatedAgents: Agent[],
) => [
{
name: agent => {
@@ -80,4 +82,49 @@ export const agentsTableActions = (
'data-test-subj': 'action-groups',
enabled: () => allowEditGroups,
},
+ {
+ name: agent => {
+ const name =
Upgrade;
+
+ const isOutdated = !!outdatedAgents.find(
+ outdatedAgent => outdatedAgent.id === agent.id,
+ );
+
+ if (agent.status === API_NAME_AGENT_STATUS.ACTIVE && isOutdated) {
+ return (
+
+ {name}
+
+ );
+ }
+
+ return (
+
+ {name}
+
+ );
+ },
+ description: 'Upgrade',
+ icon: 'package',
+ type: 'icon',
+ onClick: agent => {
+ setAgent(agent);
+ setIsUpgradeModalVisible(true);
+ },
+ 'data-test-subj': 'action-upgrade',
+ enabled: agent => {
+ const isOutdated = !!outdatedAgents.find(
+ outdatedAgent => outdatedAgent.id === agent.id,
+ );
+ return agent.status === API_NAME_AGENT_STATUS.ACTIVE && isOutdated;
+ },
+ },
];
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
new file mode 100644
index 0000000000..76ce5111ed
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.test.tsx
@@ -0,0 +1,62 @@
+import React from 'react';
+import { render, fireEvent, waitFor, act } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { UpgradeAgentModal } from './upgrade-agent-modal';
+
+jest.mock('../../services', () => ({
+ upgradeAgentsService: jest.fn(),
+}));
+
+jest.mock('../../../../react-services/common-services', () => ({
+ getErrorOrchestrator: () => ({
+ handleError: () => {},
+ }),
+}));
+
+describe('UpgradeAgentModal component', () => {
+ test('should return the component', async () => {
+ const { container, getByText, getByRole } = render(
+
{}}
+ reloadAgents={() => {}}
+ />,
+ );
+
+ expect(container).toMatchSnapshot();
+
+ const agentName = getByText('Upgrade agent agent1?');
+ expect(agentName).toBeInTheDocument();
+
+ const saveButton = getByRole('button', { name: 'Upgrade' });
+ expect(saveButton).toBeInTheDocument();
+
+ const cancelButton = getByRole('button', { name: 'Cancel' });
+ expect(cancelButton).toBeInTheDocument();
+ });
+
+ test('should send to upgrade', async () => {
+ const { getByText, getByRole } = render(
+ {}}
+ reloadAgents={() => {}}
+ />,
+ );
+
+ const saveButton = getByRole('button', { name: 'Upgrade' });
+ expect(saveButton).toBeInTheDocument();
+
+ act(() => {
+ fireEvent.click(saveButton);
+ });
+ });
+});
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
new file mode 100644
index 0000000000..0481854917
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/actions/upgrade-agent-modal.tsx
@@ -0,0 +1,79 @@
+import React, { useState } from 'react';
+import { EuiConfirmModal } from '@elastic/eui';
+import { compose } from 'redux';
+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 { Agent } from '../../types';
+import { getToasts } from '../../../../kibana-services';
+
+interface UpgradeAgentModalProps {
+ agent: Agent;
+ onClose: () => void;
+ reloadAgents: () => 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,
+ });
+ };
+
+ 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();
+ }
+ };
+
+ 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 1823b0e7c1..640e24edd1 100644
--- a/plugins/main/public/components/endpoints-summary/table/agents-table.tsx
+++ b/plugins/main/public/components/endpoints-summary/table/agents-table.tsx
@@ -25,6 +25,7 @@ import {
UI_ORDER_AGENT_STATUS,
AGENT_SYNCED_STATUS,
SEARCH_BAR_WQL_VALUE_SUGGESTIONS_COUNT,
+ UI_LOGGER_LEVELS,
} from '../../../../common/constants';
import { TableWzAPI } from '../../common/tables';
import { WzRequest } from '../../../react-services/wz-request';
@@ -39,6 +40,11 @@ import { updateCurrentAgentData } from '../../../redux/actions/appStateActions';
import { agentsTableColumns } from './columns';
import { AgentsTableGlobalActions } from './global-actions/global-actions';
import { Agent } from '../types';
+import { UpgradeAgentModal } from './actions/upgrade-agent-modal';
+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';
const searchBarWQLOptions = {
implicitQuery: {
@@ -47,6 +53,11 @@ const searchBarWQLOptions = {
},
};
+type AgentList = {
+ items: Agent[];
+ totalItems: number;
+};
+
const mapDispatchToProps = dispatch => ({
updateCurrentAgentData: data => dispatch(updateCurrentAgentData(data)),
});
@@ -54,6 +65,7 @@ const mapDispatchToProps = dispatch => ({
interface AgentsTableProps {
filters: any;
updateCurrentAgentData: (agent) => void;
+ externalReload?: boolean;
setExternalReload?: (newValue: number) => void;
}
@@ -70,16 +82,18 @@ export const AgentsTable = compose(
const [filters, setFilters] = useState(defaultFilters);
const [agent, setAgent] = useState();
const [reloadTable, setReloadTable] = useState(0);
- const [agentList, setAgentList] = useState<{
- items: Agent[];
- totalItems: number;
- }>({ items: [], totalItems: 0 });
+ const [agentList, setAgentList] = useState({
+ items: [],
+ totalItems: 0,
+ });
const [isEditGroupsVisible, setIsEditGroupsVisible] = useState(false);
+ const [isUpgradeModalVisible, setIsUpgradeModalVisible] = useState(false);
const [selectedItems, setSelectedItems] = useState([]);
const [allAgentsSelected, setAllAgentsSelected] = useState(false);
const [denyEditGroups] = useUserPermissionsRequirements([
{ action: 'group:modify_assignments', resource: 'group:id:*' },
]);
+ const [outdatedAgents, setOutdatedAgents] = useState([]);
useEffect(() => {
if (sessionStorage.getItem('wz-agents-overview-table-filter')) {
@@ -146,6 +160,31 @@ export const AgentsTable = compose(
setAllAgentsSelected(true);
};
+ const handleOnDataChange = async (data: AgentList) => {
+ setAgentList(data);
+
+ const agentIds = data?.items?.map(agent => agent.id);
+
+ try {
+ const outdatedAgents = await getOutdatedAgents(agentIds);
+ setOutdatedAgents(outdatedAgents);
+ } catch (error) {
+ setOutdatedAgents([]);
+ const options = {
+ context: `AgentsTable.getOutdatedAgents`,
+ level: UI_LOGGER_LEVELS.ERROR,
+ severity: UI_ERROR_SEVERITIES.BUSINESS,
+ store: true,
+ error: {
+ error,
+ message: error.message || error,
+ title: `Could not get outdated agents`,
+ },
+ };
+ getErrorOrchestrator().handleError(options);
+ }
+ };
+
const showSelectAllItems =
(selectedItems.length === agentList.items?.length &&
selectedItems.length < agentList.totalItems) ||
@@ -191,6 +230,7 @@ export const AgentsTable = compose(
}
actionButtons={({ filters }) => (
<>
@@ -227,7 +267,9 @@ export const AgentsTable = compose(
!denyEditGroups,
setAgent,
setIsEditGroupsVisible,
+ setIsUpgradeModalVisible,
setFilters,
+ outdatedAgents,
)}
tableInitialSortingField='id'
tablePageSizeOptions={[10, 25, 50, 100]}
@@ -251,7 +293,7 @@ export const AgentsTable = compose(
}}
rowProps={getRowProps}
filters={filters}
- onDataChange={data => setAgentList(data)}
+ onDataChange={handleOnDataChange}
downloadCsv
showReload
showFieldSelector
@@ -404,7 +446,7 @@ export const AgentsTable = compose(
return (
{table}
- {isEditGroupsVisible ? (
+ {isEditGroupsVisible && agent ? (
reloadAgents()}
@@ -414,6 +456,16 @@ export const AgentsTable = compose(
}}
/>
) : null}
+ {isUpgradeModalVisible && agent ? (
+ reloadAgents()}
+ onClose={() => {
+ setIsEditGroupsVisible(false);
+ setAgent(undefined);
+ }}
+ />
+ ) : null}
);
});
diff --git a/plugins/main/public/components/endpoints-summary/table/columns.tsx b/plugins/main/public/components/endpoints-summary/table/columns.tsx
index 7e8e404c9c..c1f1b433ba 100644
--- a/plugins/main/public/components/endpoints-summary/table/columns.tsx
+++ b/plugins/main/public/components/endpoints-summary/table/columns.tsx
@@ -4,7 +4,13 @@ import { AgentSynced } from '../../agents/agent-synced';
import { AgentStatus } from '../../agents/agent-status';
import { formatUIDate } from '../../../react-services/time-service';
import { GroupTruncate } from '../../common/util';
-import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIconTip,
+ EuiHealth,
+ EuiToolTip,
+} from '@elastic/eui';
import { Agent } from '../types';
// Columns with the property truncateText: true won't wrap the text
@@ -13,7 +19,9 @@ export const agentsTableColumns = (
allowEditGroups: boolean,
setAgent: (agents: Agent) => void,
setIsEditGroupsVisible: (visible: boolean) => void,
+ setIsUpgradeModalVisible: (visible: boolean) => void,
setFilters: (filters) => void,
+ outdatedAgents: Agent[],
) => [
{
field: 'id',
@@ -66,7 +74,29 @@ export const agentsTableColumns = (
sortable: true,
show: true,
searchable: true,
- width: '10%',
+ width: '100px',
+ render: (version, agent) => {
+ const isOutdated = !!outdatedAgents.find(
+ outdatedAgent => outdatedAgent.id === agent.id,
+ );
+ return (
+
+ {version}
+ {isOutdated ? (
+
+ Outdated}>
+
+
+
+ ) : null}
+
+ );
+ },
},
{
field: 'dateAdd',
@@ -128,6 +158,8 @@ export const agentsTableColumns = (
allowEditGroups,
setAgent,
setIsEditGroupsVisible,
+ setIsUpgradeModalVisible,
+ outdatedAgents,
),
},
];
diff --git a/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/__snapshots__/upgrades-in-progress.test.tsx.snap b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/__snapshots__/upgrades-in-progress.test.tsx.snap
new file mode 100644
index 0000000000..a91fdfedfc
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/__snapshots__/upgrades-in-progress.test.tsx.snap
@@ -0,0 +1,127 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AgentUpgradesInProgress component should return the component 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+ Upgrade tasks in progress
+
+
+
+
+
+
+
+
+
+ 2
+
+ Failed upgrade tasks
+
+
+
+
+
+
+
+
+
+
+`;
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
new file mode 100644
index 0000000000..5e1753dde7
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/taskDetailsButton.tsx
@@ -0,0 +1,224 @@
+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
new file mode 100644
index 0000000000..df4b3d67eb
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.test.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { render, fireEvent, waitFor, act } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { AgentUpgradesInProgress } from './upgrades-in-progress';
+import { useGetUpgradeTasks } from '../../hooks';
+
+jest.mock('../../hooks', () => ({
+ useGetUpgradeTasks: jest.fn(),
+}));
+
+jest.mock('../../../../react-services/common-services', () => ({
+ getErrorOrchestrator: () => ({
+ handleError: () => {},
+ }),
+}));
+
+describe('AgentUpgradesInProgress component', () => {
+ test('should return the component', async () => {
+ (useGetUpgradeTasks as jest.Mock).mockReturnValue({
+ getInProgressIsLoading: false,
+ totalInProgressTasks: 5,
+ getErrorIsLoading: false,
+ totalErrorUpgradeTasks: 2,
+ });
+
+ const { container, getByText } = render(
+ ,
+ );
+
+ expect(container).toMatchSnapshot();
+
+ const inProgressValue = getByText('5');
+ expect(inProgressValue).toBeInTheDocument();
+ const inProgressText = getByText('Upgrade tasks in progress');
+ expect(inProgressText).toBeInTheDocument();
+
+ const failedValue = getByText('2');
+ expect(failedValue).toBeInTheDocument();
+ const failedText = getByText('Failed upgrade tasks');
+ expect(failedText).toBeInTheDocument();
+ });
+
+ test('should show upgrade tasks modal', async () => {
+ const { getByRole, getByText } = render(
+ ,
+ );
+
+ const openModalButton = getByRole('button');
+ 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
new file mode 100644
index 0000000000..81b1133c94
--- /dev/null
+++ b/plugins/main/public/components/endpoints-summary/table/upgrades-in-progress/upgrades-in-progress.tsx
@@ -0,0 +1,158 @@
+import React, { useState, useEffect } from 'react';
+import {
+ EuiPanel,
+ EuiProgress,
+ EuiText,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIconTip,
+ EuiSpacer,
+} from '@elastic/eui';
+import { useGetUpgradeTasks } from '../../hooks';
+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 { AgentUpgradesTaskDetailsButton } from './taskDetailsButton';
+
+interface AgentUpgradesInProgress {
+ reload: any;
+}
+
+export const AgentUpgradesInProgress = ({
+ reload,
+}: AgentUpgradesInProgress) => {
+ const [isUpgrading, setIsUpgrading] = useState(false);
+ const {
+ totalInProgressTasks,
+ getInProgressError,
+ totalSuccessTasks,
+ getSuccessError,
+ totalErrorUpgradeTasks,
+ getErrorTasksError,
+ } = useGetUpgradeTasks(reload);
+
+ useEffect(() => {
+ if (totalInProgressTasks > 0) {
+ setIsUpgrading(true);
+ }
+ }, [totalInProgressTasks]);
+
+ if (getInProgressError) {
+ 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`,
+ },
+ };
+ getErrorOrchestrator().handleError(options);
+ }
+
+ 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);
+ }
+
+ 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);
+ }
+
+ return isUpgrading || totalSuccessTasks || totalErrorUpgradeTasks ? (
+
+
+
+ Upgrade tasks
+
+
+
+
+
+
+
+ {totalInProgressTasks > 0 ? (
+
+
+
+
+ {totalInProgressTasks}
+ {totalInProgressTasks === 1
+ ? ' Upgrade task in progress'
+ : ' Upgrade tasks in progress'}
+
+
+
+ ) : null}
+ {totalSuccessTasks > 0 ? (
+
+
+
+
+
+ {totalSuccessTasks}
+ {totalSuccessTasks === 1
+ ? ' Success upgrade task '
+ : ' Success upgrade tasks '}
+
+
+
+
+
+ ) : null}
+ {totalErrorUpgradeTasks > 0 ? (
+
+
+
+
+
+ {totalErrorUpgradeTasks}
+ {totalErrorUpgradeTasks === 1
+ ? ' Failed upgrade task '
+ : ' Failed upgrade tasks '}
+
+
+
+
+
+
+ ) : null}
+
+
+ ) : null;
+};
diff --git a/plugins/main/public/components/endpoints-summary/types.ts b/plugins/main/public/components/endpoints-summary/types.ts
index f0d3dca0da..ce9a085954 100644
--- a/plugins/main/public/components/endpoints-summary/types.ts
+++ b/plugins/main/public/components/endpoints-summary/types.ts
@@ -30,3 +30,8 @@ export type Group = {
name: string;
count: number;
};
+
+export type ResponseUpgradeAgents = {
+ agent: string;
+ task_id: number;
+};
diff --git a/plugins/main/public/components/search-bar/query-language/wql.tsx b/plugins/main/public/components/search-bar/query-language/wql.tsx
index 5282a6e096..7d139db27b 100644
--- a/plugins/main/public/components/search-bar/query-language/wql.tsx
+++ b/plugins/main/public/components/search-bar/query-language/wql.tsx
@@ -343,11 +343,13 @@ function filterTokenValueSuggestion(
suggestions: QLOptionSuggestionEntityItemTyped[],
) {
return suggestions
- .filter(({ label }: QLOptionSuggestionEntityItemTyped) => {
- const re = getTokenValueRegularExpression();
- return re.test(label);
- })
- .slice(0, SEARCH_BAR_WQL_VALUE_SUGGESTIONS_DISPLAY_COUNT);
+ ? suggestions
+ .filter(({ label }: QLOptionSuggestionEntityItemTyped) => {
+ const re = getTokenValueRegularExpression();
+ return re.test(label);
+ })
+ .slice(0, SEARCH_BAR_WQL_VALUE_SUGGESTIONS_DISPLAY_COUNT)
+ : [];
}
/**