Skip to content

Commit 8a30462

Browse files
authored
[Ingest] Agent config settings UI (#64854)
* Remove duplicate donut chart, move used donut chart closer to usage, clean up import paths * Change delete agent config to only single delete and delete all its associated data sources * Initial pass at settings tab * Reuse existing create agent config form instead * Fix delete api * Prevent nav drawer from hiding bottom bar (save action area) content * Remove delete config functionality from list page * Prevent API from deleting config with agents enrolled * Fix namespace populating in form * Display confirmation modal to deploy to agents if agents are detected * Adjust confirm delete copy * Fix i18n checks * Fix type check * Fix it again * De-dupe confirm modal * Fix i18n * Update agent config info after saving * Adjust skip unassign from agent config option schema in delete datasource method
1 parent fc2f244 commit 8a30462

File tree

31 files changed

+680
-744
lines changed

31 files changed

+680
-744
lines changed

x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ export interface UpdateAgentConfigResponse {
4949
success: boolean;
5050
}
5151

52-
export interface DeleteAgentConfigsRequest {
52+
export interface DeleteAgentConfigRequest {
5353
body: {
54-
agentConfigIds: string[];
54+
agentConfigId: string;
5555
};
5656
}
5757

58-
export type DeleteAgentConfigsResponse = Array<{
58+
export interface DeleteAgentConfigResponse {
5959
id: string;
6060
success: boolean;
61-
}>;
61+
}
6262

6363
export interface GetFullAgentConfigRequest {
6464
params: {

x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import {
1818
CreateAgentConfigResponse,
1919
UpdateAgentConfigRequest,
2020
UpdateAgentConfigResponse,
21-
DeleteAgentConfigsRequest,
22-
DeleteAgentConfigsResponse,
21+
DeleteAgentConfigRequest,
22+
DeleteAgentConfigResponse,
2323
} from '../../types';
2424

2525
export const useGetAgentConfigs = (query: HttpFetchQuery = {}) => {
@@ -75,8 +75,8 @@ export const sendUpdateAgentConfig = (
7575
});
7676
};
7777

78-
export const sendDeleteAgentConfigs = (body: DeleteAgentConfigsRequest['body']) => {
79-
return sendRequest<DeleteAgentConfigsResponse>({
78+
export const sendDeleteAgentConfig = (body: DeleteAgentConfigRequest['body']) => {
79+
return sendRequest<DeleteAgentConfigResponse>({
8080
path: agentConfigRouteService.getDeletePath(),
8181
method: 'post',
8282
body: JSON.stringify(body),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import '@elastic/eui/src/components/header/variables';
2+
@import '@elastic/eui/src/components/nav_drawer/variables';
3+
4+
/**
5+
* 1. Hack EUI so the bottom bar doesn't obscure the nav drawer flyout.
6+
*/
7+
.ingestManager__bottomBar {
8+
z-index: 0; /* 1 */
9+
left: $euiNavDrawerWidthCollapsed;
10+
}
11+
12+
.ingestManager__bottomBar-isNavDrawerLocked {
13+
left: $euiNavDrawerWidthExpanded;
14+
}

x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp
2323
import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks';
2424
import { PackageInstallProvider } from './sections/epm/hooks';
2525
import { sendSetup } from './hooks/use_request/setup';
26+
import './index.scss';
2627

2728
export interface ProtectedRouteProps extends RouteProps {
2829
isAllowed?: boolean;

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx

Lines changed: 70 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -5,125 +5,100 @@
55
*/
66

77
import React, { Fragment, useRef, useState } from 'react';
8-
import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
8+
import { EuiConfirmModal, EuiOverlayMask, EuiCallOut } from '@elastic/eui';
99
import { i18n } from '@kbn/i18n';
1010
import { FormattedMessage } from '@kbn/i18n/react';
1111
import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants';
12-
import { sendDeleteAgentConfigs, useCore, sendRequest } from '../../../hooks';
12+
import { sendDeleteAgentConfig, useCore, useConfig, sendRequest } from '../../../hooks';
1313

1414
interface Props {
15-
children: (deleteAgentConfigs: deleteAgentConfigs) => React.ReactElement;
15+
children: (deleteAgentConfig: DeleteAgentConfig) => React.ReactElement;
1616
}
1717

18-
export type deleteAgentConfigs = (agentConfigs: string[], onSuccess?: OnSuccessCallback) => void;
18+
export type DeleteAgentConfig = (agentConfig: string, onSuccess?: OnSuccessCallback) => void;
1919

20-
type OnSuccessCallback = (agentConfigsUnenrolled: string[]) => void;
20+
type OnSuccessCallback = (agentConfigDeleted: string) => void;
2121

2222
export const AgentConfigDeleteProvider: React.FunctionComponent<Props> = ({ children }) => {
2323
const { notifications } = useCore();
24-
const [agentConfigs, setAgentConfigs] = useState<string[]>([]);
24+
const {
25+
fleet: { enabled: isFleetEnabled },
26+
} = useConfig();
27+
const [agentConfig, setAgentConfig] = useState<string>();
2528
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
2629
const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState<boolean>(false);
2730
const [agentsCount, setAgentsCount] = useState<number>(0);
2831
const [isLoading, setIsLoading] = useState<boolean>(false);
2932
const onSuccessCallback = useRef<OnSuccessCallback | null>(null);
3033

31-
const deleteAgentConfigsPrompt: deleteAgentConfigs = (
32-
agentConfigsToDelete,
34+
const deleteAgentConfigPrompt: DeleteAgentConfig = (
35+
agentConfigToDelete,
3336
onSuccess = () => undefined
3437
) => {
35-
if (
36-
agentConfigsToDelete === undefined ||
37-
(Array.isArray(agentConfigsToDelete) && agentConfigsToDelete.length === 0)
38-
) {
39-
throw new Error('No agent configs specified for deletion');
38+
if (!agentConfigToDelete) {
39+
throw new Error('No agent config specified for deletion');
4040
}
4141
setIsModalOpen(true);
42-
setAgentConfigs(agentConfigsToDelete);
43-
fetchAgentsCount(agentConfigsToDelete);
42+
setAgentConfig(agentConfigToDelete);
43+
fetchAgentsCount(agentConfigToDelete);
4444
onSuccessCallback.current = onSuccess;
4545
};
4646

4747
const closeModal = () => {
48-
setAgentConfigs([]);
48+
setAgentConfig(undefined);
4949
setIsLoading(false);
5050
setIsLoadingAgentsCount(false);
5151
setIsModalOpen(false);
5252
};
5353

54-
const deleteAgentConfigs = async () => {
54+
const deleteAgentConfig = async () => {
5555
setIsLoading(true);
5656

5757
try {
58-
const { data } = await sendDeleteAgentConfigs({
59-
agentConfigIds: agentConfigs,
58+
const { data } = await sendDeleteAgentConfig({
59+
agentConfigId: agentConfig!,
6060
});
61-
const successfulResults = data?.filter(result => result.success) || [];
62-
const failedResults = data?.filter(result => !result.success) || [];
6361

64-
if (successfulResults.length) {
65-
const hasMultipleSuccesses = successfulResults.length > 1;
66-
const successMessage = hasMultipleSuccesses
67-
? i18n.translate(
68-
'xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle',
69-
{
70-
defaultMessage: 'Deleted {count} agent configs',
71-
values: { count: successfulResults.length },
72-
}
73-
)
74-
: i18n.translate(
75-
'xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle',
76-
{
77-
defaultMessage: "Deleted agent config '{id}'",
78-
values: { id: successfulResults[0].id },
79-
}
80-
);
81-
notifications.toasts.addSuccess(successMessage);
62+
if (data?.success) {
63+
notifications.toasts.addSuccess(
64+
i18n.translate('xpack.ingestManager.deleteAgentConfig.successSingleNotificationTitle', {
65+
defaultMessage: "Deleted agent config '{id}'",
66+
values: { id: agentConfig },
67+
})
68+
);
69+
if (onSuccessCallback.current) {
70+
onSuccessCallback.current(agentConfig!);
71+
}
8272
}
8373

84-
if (failedResults.length) {
85-
const hasMultipleFailures = failedResults.length > 1;
86-
const failureMessage = hasMultipleFailures
87-
? i18n.translate(
88-
'xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle',
89-
{
90-
defaultMessage: 'Error deleting {count} agent configs',
91-
values: { count: failedResults.length },
92-
}
93-
)
94-
: i18n.translate(
95-
'xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle',
96-
{
97-
defaultMessage: "Error deleting agent config '{id}'",
98-
values: { id: failedResults[0].id },
99-
}
100-
);
101-
notifications.toasts.addDanger(failureMessage);
102-
}
103-
104-
if (onSuccessCallback.current) {
105-
onSuccessCallback.current(successfulResults.map(result => result.id));
74+
if (!data?.success) {
75+
notifications.toasts.addDanger(
76+
i18n.translate('xpack.ingestManager.deleteAgentConfig.failureSingleNotificationTitle', {
77+
defaultMessage: "Error deleting agent config '{id}'",
78+
values: { id: agentConfig },
79+
})
80+
);
10681
}
10782
} catch (e) {
10883
notifications.toasts.addDanger(
109-
i18n.translate('xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle', {
110-
defaultMessage: 'Error deleting agent configs',
84+
i18n.translate('xpack.ingestManager.deleteAgentConfig.fatalErrorNotificationTitle', {
85+
defaultMessage: 'Error deleting agent config',
11186
})
11287
);
11388
}
11489
closeModal();
11590
};
11691

117-
const fetchAgentsCount = async (agentConfigsToCheck: string[]) => {
118-
if (isLoadingAgentsCount) {
92+
const fetchAgentsCount = async (agentConfigToCheck: string) => {
93+
if (!isFleetEnabled || isLoadingAgentsCount) {
11994
return;
12095
}
12196
setIsLoadingAgentsCount(true);
12297
const { data } = await sendRequest<{ total: number }>({
12398
path: `/api/ingest_manager/fleet/agents`,
12499
method: 'get',
125100
query: {
126-
kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : (${agentConfigsToCheck.join(' or ')})`,
101+
kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : ${agentConfigToCheck}`,
127102
},
128103
});
129104
setAgentsCount(data?.total || 0);
@@ -140,68 +115,61 @@ export const AgentConfigDeleteProvider: React.FunctionComponent<Props> = ({ chil
140115
<EuiConfirmModal
141116
title={
142117
<FormattedMessage
143-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle"
144-
defaultMessage="Delete {count, plural, one {this agent config} other {# agent configs}}?"
145-
values={{ count: agentConfigs.length }}
118+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.deleteConfigTitle"
119+
defaultMessage="Delete this agent configuration?"
146120
/>
147121
}
148122
onCancel={closeModal}
149-
onConfirm={deleteAgentConfigs}
123+
onConfirm={deleteAgentConfig}
150124
cancelButtonText={
151125
<FormattedMessage
152-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel"
126+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.cancelButtonLabel"
153127
defaultMessage="Cancel"
154128
/>
155129
}
156130
confirmButtonText={
157131
isLoading || isLoadingAgentsCount ? (
158132
<FormattedMessage
159-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel"
133+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.loadingButtonLabel"
160134
defaultMessage="Loading…"
161135
/>
162-
) : agentsCount ? (
163-
<FormattedMessage
164-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel"
165-
defaultMessage="Delete {agentConfigsCount, plural, one {agent config} other {agent configs}} and unenroll {agentsCount, plural, one {agent} other {agents}}"
166-
values={{
167-
agentsCount,
168-
agentConfigsCount: agentConfigs.length,
169-
}}
170-
/>
171136
) : (
172137
<FormattedMessage
173-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel"
174-
defaultMessage="Delete {agentConfigsCount, plural, one {agent config} other {agent configs}}"
175-
values={{
176-
agentConfigsCount: agentConfigs.length,
177-
}}
138+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.confirmButtonLabel"
139+
defaultMessage="Delete configuration"
178140
/>
179141
)
180142
}
181143
buttonColor="danger"
182-
confirmButtonDisabled={isLoading || isLoadingAgentsCount}
144+
confirmButtonDisabled={isLoading || isLoadingAgentsCount || !!agentsCount}
183145
>
184146
{isLoadingAgentsCount ? (
185147
<FormattedMessage
186-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage"
148+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.loadingAgentsCountMessage"
187149
defaultMessage="Checking amount of affected agents…"
188150
/>
189151
) : agentsCount ? (
190-
<FormattedMessage
191-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage"
192-
defaultMessage="{agentsCount, plural, one {# agent is} other {# agents are}} assigned {agentConfigsCount, plural, one {to this agent config} other {across these agentConfigs}}. {agentsCount, plural, one {This agent} other {These agents}} will be unenrolled."
193-
values={{
194-
agentsCount,
195-
agentConfigsCount: agentConfigs.length,
196-
}}
197-
/>
152+
<EuiCallOut
153+
color="danger"
154+
title={i18n.translate(
155+
'xpack.ingestManager.deleteAgentConfig.confirmModal.affectedAgentsTitle',
156+
{
157+
defaultMessage: 'Configuration in use',
158+
}
159+
)}
160+
>
161+
<FormattedMessage
162+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.affectedAgentsMessage"
163+
defaultMessage="{agentsCount, plural, one {# agent is} other {# agents are}} assigned to this agent configuration. Unassign these agents before deleting this configuration."
164+
values={{
165+
agentsCount,
166+
}}
167+
/>
168+
</EuiCallOut>
198169
) : (
199170
<FormattedMessage
200-
id="xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage"
201-
defaultMessage="There are no agents assigned to {agentConfigsCount, plural, one {this agent config} other {these agentConfigs}}."
202-
values={{
203-
agentConfigsCount: agentConfigs.length,
204-
}}
171+
id="xpack.ingestManager.deleteAgentConfig.confirmModal.irreversibleMessage"
172+
defaultMessage="This action cannot be undone."
205173
/>
206174
)}
207175
</EuiConfirmModal>
@@ -211,7 +179,7 @@ export const AgentConfigDeleteProvider: React.FunctionComponent<Props> = ({ chil
211179

212180
return (
213181
<Fragment>
214-
{children(deleteAgentConfigsPrompt)}
182+
{children(deleteAgentConfigPrompt)}
215183
{renderModal()}
216184
</Fragment>
217185
);

0 commit comments

Comments
 (0)