Skip to content

Commit

Permalink
[Ingest Manager] Change agent config YAML view to flyout (#67918)
Browse files Browse the repository at this point in the history
* Consolidate context menu/row action components and usage, and some export/imports

* Missed export

* Move agent config yaml tab to flyout, add and consolidate agent config action menus

* Fix i18n

* Add download config YAML endpoint and link from flyout, add common config->yaml service

* Actually remove the tab lol

* Fix i18n
  • Loading branch information
jen-huang authored Jun 2, 2020
1 parent 0247df4 commit eab74f4
Show file tree
Hide file tree
Showing 30 changed files with 481 additions and 373 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 @@ -46,6 +46,7 @@ export const AGENT_CONFIG_API_ROUTES = {
UPDATE_PATTERN: `${AGENT_CONFIG_API_ROOT}/{agentConfigId}`,
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`,
};

// Output API routes
Expand Down
39 changes: 39 additions & 0 deletions x-pack/plugins/ingest_manager/common/services/config_to_yaml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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 { safeDump } from 'js-yaml';
import { FullAgentConfig } from '../types';

const CONFIG_KEYS_ORDER = [
'id',
'name',
'revision',
'type',
'outputs',
'settings',
'datasources',
'enabled',
'package',
'input',
];

export const configToYaml = (config: FullAgentConfig): string => {
return safeDump(config, {
skipInvalid: true,
sortKeys: (keyA: string, keyB: string) => {
const indexA = CONFIG_KEYS_ORDER.indexOf(keyA);
const indexB = CONFIG_KEYS_ORDER.indexOf(keyB);
if (indexA >= 0 && indexB < 0) {
return -1;
}

if (indexA < 0 && indexB >= 0) {
return 1;
}

return indexA - indexB;
},
});
};
1 change: 1 addition & 0 deletions x-pack/plugins/ingest_manager/common/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import * as AgentStatusKueryHelper from './agent_status';
export * from './routes';
export { packageToConfigDatasourceInputs, packageToConfigDatasource } from './package_to_config';
export { storedDatasourceToAgentDatasource } from './datasource_to_agent_datasource';
export { configToYaml } from './config_to_yaml';
export { AgentStatusKueryHelper };
export { decodeCloudId } from './decode_cloud_id';
7 changes: 7 additions & 0 deletions x-pack/plugins/ingest_manager/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ export const agentConfigRouteService = {
getInfoFullPath: (agentConfigId: string) => {
return AGENT_CONFIG_API_ROUTES.FULL_INFO_PATTERN.replace('{agentConfigId}', agentConfigId);
},

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

export const dataStreamRouteService = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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, { useCallback, useState } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiButtonIcon,
EuiContextMenu,
EuiContextMenuPanel,
EuiPopover,
EuiButton,
} from '@elastic/eui';
import { EuiButtonProps } from '@elastic/eui/src/components/button/button';
import { EuiContextMenuProps } from '@elastic/eui/src/components/context_menu/context_menu';
import { EuiContextMenuPanelProps } from '@elastic/eui/src/components/context_menu/context_menu_panel';

type Props = {
button?: {
props: EuiButtonProps;
children: JSX.Element;
};
} & (
| {
items: EuiContextMenuPanelProps['items'];
}
| {
panels: EuiContextMenuProps['panels'];
}
);

export const ContextMenuActions = React.memo<Props>(({ button, ...props }) => {
const [isOpen, setIsOpen] = useState(false);
const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]);
const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]);

return (
<EuiPopover
anchorPosition="downRight"
panelPaddingSize="none"
button={
button ? (
<EuiButton {...button.props} onClick={handleToggleMenu}>
{button.children}
</EuiButton>
) : (
<EuiButtonIcon
iconType="boxesHorizontal"
onClick={handleToggleMenu}
aria-label={i18n.translate('xpack.ingestManager.genericActionsMenuText', {
defaultMessage: 'Open',
})}
/>
)
}
isOpen={isOpen}
closePopover={handleCloseMenu}
>
{'items' in props ? (
<EuiContextMenuPanel items={props.items} />
) : (
<EuiContextMenu panels={props.panels} initialPanelId={0} />
)}
</EuiPopover>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ export { Loading } from './loading';
export { Error } from './error';
export { Header, HeaderProps } from './header';
export { AlphaMessaging } from './alpha_messaging';
export { PackageIcon } from './package_icon';
export { ContextMenuActions } from './context_menu_actions';
export { SearchBar } from './search_bar';
export * from './settings_flyout';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export const PAGE_ROUTING_PATHS = {
configurations: '/configs',
configurations_list: '/configs',
configuration_details: '/configs/:configId/:tabId?',
configuration_details_yaml: '/configs/:configId/yaml',
configuration_details_settings: '/configs/:configId/settings',
add_datasource_from_configuration: '/configs/:configId/add-datasource',
add_datasource_from_integration: '/integrations/:pkgkey/add-datasource',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { useConfig, ConfigContext } from './use_config';
export { useSetupDeps, useStartDeps, DepsContext } from './use_deps';
export { useBreadcrumbs } from './use_breadcrumbs';
export { useLink } from './use_link';
export { useKibanaLink } from './use_kibana_link';
export { usePackageIconType, UsePackageIconType } from './use_package_icon_type';
export { usePagination, Pagination } from './use_pagination';
export { useDebounce } from './use_debounce';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { agentConfigRouteService } from '../../services';
import {
GetAgentConfigsResponse,
GetOneAgentConfigResponse,
GetFullAgentConfigResponse,
CreateAgentConfigRequest,
CreateAgentConfigResponse,
UpdateAgentConfigRequest,
Expand All @@ -39,7 +40,7 @@ export const useGetOneAgentConfig = (agentConfigId: string | undefined) => {
};

export const useGetOneAgentConfigFull = (agentConfigId: string) => {
return useRequest({
return useRequest<GetFullAgentConfigResponse>({
path: agentConfigRouteService.getInfoFullPath(agentConfigId),
method: 'get',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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, { memo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiContextMenuItem, EuiPortal } from '@elastic/eui';
import { useCapabilities, useLink } from '../../../hooks';
import { ContextMenuActions } from '../../../components';
import { ConfigYamlFlyout } from './config_yaml_flyout';

export const AgentConfigActionMenu = memo<{ configId: string; fullButton?: boolean }>(
({ configId, fullButton = false }) => {
const { getHref } = useLink();
const hasWriteCapabilities = useCapabilities().write;
const [isYamlFlyoutOpen, setIsYamlFlyoutOpen] = useState<boolean>(false);
return (
<>
{isYamlFlyoutOpen ? (
<EuiPortal>
<ConfigYamlFlyout configId={configId} onClose={() => setIsYamlFlyoutOpen(false)} />
</EuiPortal>
) : null}
<ContextMenuActions
button={
fullButton
? {
props: {
iconType: 'arrowDown',
iconSide: 'right',
},
children: (
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.buttonText"
defaultMessage="Actions"
/>
),
}
: undefined
}
items={[
<EuiContextMenuItem
icon="inspect"
onClick={() => setIsYamlFlyoutOpen(!isYamlFlyoutOpen)}
key="viewConfig"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.viewConfigText"
defaultMessage="View config"
/>
</EuiContextMenuItem>,
<EuiContextMenuItem
disabled={!hasWriteCapabilities}
icon="plusInCircle"
href={getHref('add_datasource_from_configuration', { configId })}
key="createDatasource"
>
<FormattedMessage
id="xpack.ingestManager.agentConfigActionMenu.createDatasourceActionText"
defaultMessage="Create data source"
/>
</EuiContextMenuItem>,
]}
/>
</>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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, { memo } from 'react';
import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiCodeBlock,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
EuiFlyoutHeader,
EuiTitle,
EuiFlyoutBody,
EuiFlyoutFooter,
EuiButtonEmpty,
EuiButton,
} from '@elastic/eui';
import { useGetOneAgentConfigFull, useGetOneAgentConfig, useCore } from '../../../hooks';
import { Loading } from '../../../components';
import { configToYaml, agentConfigRouteService } from '../../../services';

const FlyoutBody = styled(EuiFlyoutBody)`
.euiFlyoutBody__overflowContent {
padding: 0;
}
`;

export const ConfigYamlFlyout = memo<{ configId: string; onClose: () => void }>(
({ configId, onClose }) => {
const core = useCore();
const { isLoading: isLoadingYaml, data: yamlData } = useGetOneAgentConfigFull(configId);
const { data: configData } = useGetOneAgentConfig(configId);

const body =
isLoadingYaml && !yamlData ? (
<Loading />
) : (
<EuiCodeBlock language="yaml" isCopyable fontSize="m">
{configToYaml(yamlData!.item)}
</EuiCodeBlock>
);

const downloadLink = core.http.basePath.prepend(
agentConfigRouteService.getInfoFullDownloadPath(configId)
);

return (
<EuiFlyout onClose={onClose} size="l" maxWidth={640}>
<EuiFlyoutHeader hasBorder aria-labelledby="IngestManagerConfigYamlFlyoutTitle">
<EuiTitle size="m">
<h2 id="IngestManagerConfigYamlFlyoutTitle">
{configData?.item ? (
<FormattedMessage
id="xpack.ingestManager.configDetails.yamlflyoutTitleWithName"
defaultMessage="'{name}' agent configuration"
values={{ name: configData.item.name }}
/>
) : (
<FormattedMessage
id="xpack.ingestManager.configDetails.yamlflyoutTitleWithoutName"
defaultMessage="Agent configuration"
/>
)}
</h2>
</EuiTitle>
</EuiFlyoutHeader>
<FlyoutBody>{body}</FlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty iconType="cross" onClick={onClose} flush="left">
<FormattedMessage
id="xpack.ingestManager.configDetails.yamlFlyoutCloseButtonLabel"
defaultMessage="Close"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
href={downloadLink}
iconType="download"
isDisabled={Boolean(isLoadingYaml && !yamlData)}
>
<FormattedMessage
id="xpack.ingestManager.configDetails.yamlDownloadButtonLabel"
defaultMessage="Download configuration"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</EuiFlyout>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { AgentConfigForm, agentConfigFormValidation } from './config_form';
export { AgentConfigDeleteProvider } from './config_delete_provider';
export { DatasourceDeleteProvider } from './datasource_delete_provider';
export { LinkedAgentCount } from './linked_agent_count';
export { ConfirmDeployConfigModal } from './confirm_deploy_modal';
export { DangerEuiContextMenuItem } from './danger_eui_context_menu_item';
export { AgentConfigActionMenu } from './actions_menu';
Loading

0 comments on commit eab74f4

Please sign in to comment.