Skip to content

Commit

Permalink
Merge branch 'master' into implement/ftr-docker-servers
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine authored Jun 15, 2020
2 parents a3ad46a + 4f5e279 commit 82697e3
Show file tree
Hide file tree
Showing 23 changed files with 861 additions and 640 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/ingest_manager/common/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const AGENT_CONFIG_API_ROUTES = {
INFO_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}`,
CREATE_PATTERN: `${AGENT_CONFIG_API_ROOT}`,
UPDATE_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}`,
COPY_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}/copy`,
DELETE_PATTERN: `${AGENT_CONFIG_API_ROOT}/delete`,
FULL_INFO_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}/full`,
FULL_INFO_DOWNLOAD_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}/download`,
Expand Down
806 changes: 254 additions & 552 deletions x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions x-pack/plugins/ingest_manager/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export const agentConfigRouteService = {
return AGENT_CONFIG_API_ROUTES.UPDATE_PATTERN.replace('{agentConfigId}', agentConfigId);
},

getCopyPath: (agentConfigId: string) => {
return AGENT_CONFIG_API_ROUTES.COPY_PATTERN.replace('{agentConfigId}', agentConfigId);
},

getDeletePath: () => {
return AGENT_CONFIG_API_ROUTES.DELETE_PATTERN;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ export interface UpdateAgentConfigResponse {
success: boolean;
}

export interface CopyAgentConfigRequest {
body: Pick<AgentConfig, 'name' | 'description'>;
}

export interface CopyAgentConfigResponse {
item: AgentConfig;
success: boolean;
}

export interface DeleteAgentConfigRequest {
body: {
agentConfigId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
CreateAgentConfigResponse,
UpdateAgentConfigRequest,
UpdateAgentConfigResponse,
CopyAgentConfigRequest,
CopyAgentConfigResponse,
DeleteAgentConfigRequest,
DeleteAgentConfigResponse,
} from '../../types';
Expand Down Expand Up @@ -76,6 +78,17 @@ export const sendUpdateAgentConfig = (
});
};

export const sendCopyAgentConfig = (
agentConfigId: string,
body: CopyAgentConfigRequest['body']
) => {
return sendRequest<CopyAgentConfigResponse>({
path: agentConfigRouteService.getCopyPath(agentConfigId),
method: 'post',
body: JSON.stringify(body),
});
};

export const sendDeleteAgentConfig = (body: DeleteAgentConfigRequest['body']) => {
return sendRequest<DeleteAgentConfigResponse>({
path: agentConfigRouteService.getDeletePath(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,69 +11,92 @@ import { useCapabilities } from '../../../hooks';
import { ContextMenuActions } from '../../../components';
import { AgentEnrollmentFlyout } from '../../fleet/components';
import { ConfigYamlFlyout } from './config_yaml_flyout';
import { AgentConfigCopyProvider } from './config_copy_provider';

export const AgentConfigActionMenu = memo<{ config: AgentConfig; fullButton?: boolean }>(
({ config, fullButton = false }) => {
const hasWriteCapabilities = useCapabilities().write;
const [isYamlFlyoutOpen, setIsYamlFlyoutOpen] = useState<boolean>(false);
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState<boolean>(false);
return (
<>
{isYamlFlyoutOpen ? (
<EuiPortal>
<ConfigYamlFlyout configId={config.id} onClose={() => setIsYamlFlyoutOpen(false)} />
</EuiPortal>
) : null}
{isEnrollmentFlyoutOpen && (
<EuiPortal>
<AgentEnrollmentFlyout
agentConfigs={[config]}
onClose={() => setIsEnrollmentFlyoutOpen(false)}
export const AgentConfigActionMenu = memo<{
config: AgentConfig;
onCopySuccess?: (newAgentConfig: AgentConfig) => void;
fullButton?: boolean;
}>(({ config, onCopySuccess, fullButton = false }) => {
const hasWriteCapabilities = useCapabilities().write;
const [isYamlFlyoutOpen, setIsYamlFlyoutOpen] = useState<boolean>(false);
const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState<boolean>(false);

return (
<AgentConfigCopyProvider>
{(copyAgentConfigPrompt) => {
return (
<>
{isYamlFlyoutOpen ? (
<EuiPortal>
<ConfigYamlFlyout configId={config.id} onClose={() => setIsYamlFlyoutOpen(false)} />
</EuiPortal>
) : null}
{isEnrollmentFlyoutOpen && (
<EuiPortal>
<AgentEnrollmentFlyout
agentConfigs={[config]}
onClose={() => setIsEnrollmentFlyoutOpen(false)}
/>
</EuiPortal>
)}
<ContextMenuActions
button={
fullButton
? {
props: {
iconType: 'arrowDown',
iconSide: 'right',
},
children: (
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.buttonText"
defaultMessage="Actions"
/>
),
}
: undefined
}
items={[
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="plusInCircle"
onClick={() => setIsEnrollmentFlyoutOpen(true)}
key="enrollAgents"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.enrollAgentActionText"
defaultMessage="Enroll agent"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="inspect"
onClick={() => setIsYamlFlyoutOpen(!isYamlFlyoutOpen)}
key="viewConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.viewConfigText"
defaultMessage="View config"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="copy"
onClick={() => {
copyAgentConfigPrompt(config, onCopySuccess);
}}
key="copyConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.copyConfigActionText"
defaultMessage="Copy config"
/>
</EuiContextMenuItem>,
]}
/>
</EuiPortal>
)}
<ContextMenuActions
button={
fullButton
? {
props: {
iconType: 'arrowDown',
iconSide: 'right',
},
children: (
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.buttonText"
defaultMessage="Actions"
/>
),
}
: undefined
}
items={[
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="plusInCircle"
onClick={() => setIsEnrollmentFlyoutOpen(true)}
key="enrollAgents"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.enrollAgentActionText"
defaultMessage="Enroll agent"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
icon="inspect"
onClick={() => setIsYamlFlyoutOpen(!isYamlFlyoutOpen)}
key="viewConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.viewConfigText"
defaultMessage="View config"
/>
</EuiContextMenuItem>,
]}
/>
</>
);
}
);
</>
);
}}
</AgentConfigCopyProvider>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Fragment, useRef, useState } from 'react';
import { EuiConfirmModal, EuiOverlayMask, EuiFormRow, EuiFieldText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { AgentConfig } from '../../../types';
import { sendCopyAgentConfig, useCore } from '../../../hooks';

interface Props {
children: (copyAgentConfig: CopyAgentConfig) => React.ReactElement;
}

export type CopyAgentConfig = (agentConfig: AgentConfig, onSuccess?: OnSuccessCallback) => void;

type OnSuccessCallback = (newAgentConfig: AgentConfig) => void;

export const AgentConfigCopyProvider: React.FunctionComponent<Props> = ({ children }) => {
const { notifications } = useCore();
const [agentConfig, setAgentConfig] = useState<AgentConfig>();
const [newAgentConfig, setNewAgentConfig] = useState<Pick<AgentConfig, 'name' | 'description'>>();
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const onSuccessCallback = useRef<OnSuccessCallback | null>(null);

const copyAgentConfigPrompt: CopyAgentConfig = (
agentConfigToCopy,
onSuccess = () => undefined
) => {
if (!agentConfigToCopy) {
throw new Error('No agent config specified to copy');
}
setIsModalOpen(true);
setAgentConfig(agentConfigToCopy);
setNewAgentConfig({
name: i18n.translate(
'xpack.ingestManager.copyAgentConfig.confirmModal.defaultNewConfigName',
{
defaultMessage: '{name} (copy)',
values: { name: agentConfigToCopy.name },
}
),
description: agentConfigToCopy.description,
});
onSuccessCallback.current = onSuccess;
};

const closeModal = () => {
setAgentConfig(undefined);
setNewAgentConfig(undefined);
setIsLoading(false);
setIsModalOpen(false);
};

const copyAgentConfig = async () => {
setIsLoading(true);
try {
const { data } = await sendCopyAgentConfig(agentConfig!.id, newAgentConfig!);

if (data?.success) {
notifications.toasts.addSuccess(
i18n.translate('xpack.ingestManager.copyAgentConfig.successNotificationTitle', {
defaultMessage: 'Agent config copied',
})
);
if (onSuccessCallback.current) {
onSuccessCallback.current(data.item);
}
}

if (!data?.success) {
notifications.toasts.addDanger(
i18n.translate('xpack.ingestManager.copyAgentConfig.failureNotificationTitle', {
defaultMessage: "Error copying agent config '{id}'",
values: { id: agentConfig!.id },
})
);
}
} catch (e) {
notifications.toasts.addDanger(
i18n.translate('xpack.ingestManager.copyAgentConfig.fatalErrorNotificationTitle', {
defaultMessage: 'Error copying agent config',
})
);
}
closeModal();
};

const renderModal = () => {
if (!isModalOpen || !agentConfig || !newAgentConfig) {
return null;
}

return (
<EuiOverlayMask>
<EuiConfirmModal
title={
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.copyConfigTitle"
defaultMessage="Copy '{name}' agent configuration"
values={{
name: agentConfig.name,
}}
/>
}
onCancel={closeModal}
onConfirm={copyAgentConfig}
cancelButtonText={
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.cancelButtonLabel"
defaultMessage="Cancel"
/>
}
confirmButtonText={
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.confirmButtonLabel"
defaultMessage="Copy configuration"
/>
}
confirmButtonDisabled={isLoading || !newAgentConfig.name.trim()}
>
<p>
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.copyConfigPrompt"
defaultMessage="Choose a name and description for your new agent configuration."
/>
</p>
<EuiFormRow
label={
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.newNameLabel"
defaultMessage="New configuration name"
/>
}
fullWidth
>
<EuiFieldText
fullWidth
value={newAgentConfig.name}
onChange={(e) => setNewAgentConfig({ ...newAgentConfig, name: e.target.value })}
/>
</EuiFormRow>
<EuiFormRow
label={
<FormattedMessage
id="xpack.ingestManager.copyAgentConfig.confirmModal.newDescriptionLabel"
defaultMessage="Description"
/>
}
fullWidth
>
<EuiFieldText
fullWidth
value={newAgentConfig.description}
onChange={(e) =>
setNewAgentConfig({ ...newAgentConfig, description: e.target.value })
}
/>
</EuiFormRow>
</EuiConfirmModal>
</EuiOverlayMask>
);
};

return (
<Fragment>
{children(copyAgentConfigPrompt)}
{renderModal()}
</Fragment>
);
};
Loading

0 comments on commit 82697e3

Please sign in to comment.