Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution][Endpoint] New endpoint policy response UI and fleet UI for integrations in agent details page #133405

Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* 2.0.
*/

import React, { memo, useMemo } from 'react';
import type { EuiBasicTableProps } from '@elastic/eui';
import React, { memo, useMemo, useState } from 'react';
import {
EuiFlexGroup,
EuiFlexItem,
Expand All @@ -15,25 +14,40 @@ import {
EuiTitle,
EuiToolTip,
EuiPanel,
EuiButtonIcon,
EuiBasicTable,
EuiSpacer,
EuiText,
EuiTreeView,
EuiBadge,
useEuiTheme,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import styled from 'styled-components';

import type { Agent, AgentPolicy, PackagePolicy, PackagePolicyInput } from '../../../../../types';
import type { Agent, AgentPolicy, PackagePolicy } from '../../../../../types';
import { useLink, useUIExtension } from '../../../../../hooks';
import { ExtensionWrapper, PackageIcon } from '../../../../../components';

import { displayInputType, getLogsQueryByInputType } from './input_type_utils';

const StyledEuiAccordion = styled(EuiAccordion)`
.ingest-integration-title-button {
padding: ${(props) => props.theme.eui.paddingSizes.m};
.euiAccordion__button {
width: 90%;
}

.euiAccordion__triggerWrapper {
padding-left: ${(props) => props.theme.eui.paddingSizes.m};
}

&.euiAccordion-isOpen {
.euiAccordion__childWrapper {
padding: ${(props) => props.theme.eui.paddingSizes.m};
padding-top: 0px;
}
}

&.euiAccordion-isOpen .ingest-integration-title-button {
border-bottom: 1px solid ${(props) => props.theme.eui.euiColorLightShade};
.ingest-integration-title-button {
padding: ${(props) => props.theme.eui.paddingSizes.s};
}

.euiTableRow:last-child .euiTableRowCell {
Expand All @@ -43,6 +57,14 @@ const StyledEuiAccordion = styled(EuiAccordion)`
.euiIEFlexWrapFix {
min-width: 0;
}

.euiAccordion__buttonContent {
width: 100%;
}
`;

const StyledEuiLink = styled(EuiLink)`
font-size: ${(props) => props.theme.eui.euiFontSizeS};
`;

const CollapsablePanel: React.FC<{ id: string; title: React.ReactNode }> = ({
Expand All @@ -54,7 +76,7 @@ const CollapsablePanel: React.FC<{ id: string; title: React.ReactNode }> = ({
<EuiPanel paddingSize="none">
<StyledEuiAccordion
id={id}
arrowDisplay="right"
arrowDisplay="left"
buttonClassName="ingest-integration-title-button"
buttonContent={title}
>
Expand All @@ -70,55 +92,75 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
packagePolicy: PackagePolicy;
}> = memo(({ agent, agentPolicy, packagePolicy }) => {
const { getHref } = useLink();
const theme = useEuiTheme();

const [showNeedsAttentionBadge, setShowNeedsAttentionBadge] = useState(false);
const extensionView = useUIExtension(
packagePolicy.package?.name ?? '',
'package-policy-response'
);

const inputs = useMemo(() => {
return packagePolicy.inputs.filter((input) => input.enabled);
}, [packagePolicy.inputs]);
const policyResponseExtensionView = useMemo(() => {
return (
extensionView && (
<ExtensionWrapper>
<extensionView.Component
agent={agent}
onShowNeedsAttentionBadge={setShowNeedsAttentionBadge}
/>
</ExtensionWrapper>
)
);
}, [agent, extensionView]);

const columns: EuiBasicTableProps<PackagePolicyInput>['columns'] = [
const inputItems = [
{
field: 'type',
width: '100%',
name: i18n.translate('xpack.fleet.agentDetailsIntegrations.inputTypeLabel', {
defaultMessage: 'Input',
}),
render: (inputType: string) => {
return displayInputType(inputType);
},
},
{
align: 'right',
name: i18n.translate('xpack.fleet.agentDetailsIntegrations.actionsLabel', {
defaultMessage: 'Actions',
}),
field: 'type',
width: 'auto',
render: (inputType: string) => {
return (
<EuiToolTip
content={i18n.translate('xpack.fleet.agentDetailsIntegrations.viewLogsButton', {
defaultMessage: 'View logs',
})}
>
<EuiButtonIcon
href={getHref('agent_details', {
agentId: agent.id,
tabId: 'logs',
logQuery: getLogsQueryByInputType(inputType),
})}
iconType="editorAlignLeft"
aria-label={i18n.translate('xpack.fleet.agentDetailsIntegrations.viewLogsButton', {
defaultMessage: 'View logs',
})}
/>
</EuiToolTip>
);
},
label: (
<EuiText size="s">
<FormattedMessage
id="xpack.fleet.agentDetailsIntegrations.inputsTypeLabel"
defaultMessage="Inputs"
/>
</EuiText>
),
id: 'inputs',
children: packagePolicy.inputs.reduce(
(acc: Array<{ label: JSX.Element; id: string }>, current) => {
Comment on lines +127 to +128
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 🔥 . No need to change but for TS you can also do .reduce<Array<{ label: JSX.Element; id: string }>>(acc, current).

if (current.enabled) {
return [
...acc,
{
label: (
<EuiToolTip
content={i18n.translate('xpack.fleet.agentDetailsIntegrations.viewLogsButton', {
defaultMessage: 'View logs',
})}
>
<StyledEuiLink
href={getHref('agent_details', {
agentId: agent.id,
tabId: 'logs',
logQuery: getLogsQueryByInputType(current.type),
})}
aria-label={i18n.translate(
'xpack.fleet.agentDetailsIntegrations.viewLogsButton',
{
defaultMessage: 'View logs',
}
)}
>
{displayInputType(current.type)}
</StyledEuiLink>
</EuiToolTip>
),
id: current.type,
},
];
}
return acc;
},
[]
),
},
];

Expand All @@ -128,7 +170,7 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
title={
<EuiTitle size="xs">
<h3>
<EuiFlexGroup gutterSize="s">
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
{packagePolicy.package ? (
<PackageIcon
Expand All @@ -153,17 +195,29 @@ export const AgentDetailsIntegration: React.FunctionComponent<{
{packagePolicy.name}
</EuiLink>
</EuiFlexItem>
{showNeedsAttentionBadge && (
<EuiFlexItem grow={false}>
<EuiBadge color={theme.euiTheme.colors.danger} iconType="alert" iconSide="left">
<FormattedMessage
id="xpack.fleet.agentDetailsIntegrations.needsAttention.label"
defaultMessage="Needs attention"
/>
</EuiBadge>
</EuiFlexItem>
)}
</EuiFlexGroup>
</h3>
</EuiTitle>
}
>
<EuiBasicTable<PackagePolicyInput> tableLayout="auto" items={inputs} columns={columns} />
{extensionView && (
<ExtensionWrapper>
<extensionView.Component endpointId={agent.id} />
</ExtensionWrapper>
)}
<EuiTreeView
items={inputItems}
showExpansionArrows
aria-label="inputsTreeView"
aria-labelledby="inputsTreeView"
/>
{policyResponseExtensionView}
<EuiSpacer />
</CollapsablePanel>
);
});
Expand Down
8 changes: 5 additions & 3 deletions x-pack/plugins/fleet/public/types/ui_extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import type { EuiStepProps } from '@elastic/eui';
import type { ComponentType, LazyExoticComponent } from 'react';

import type { NewPackagePolicy, PackageInfo, PackagePolicy } from '.';
import type { Agent, NewPackagePolicy, PackageInfo, PackagePolicy } from '.';

/** Register a Fleet UI extension */
export type UIExtensionRegistrationCallback = (extensionPoint: UIExtensionPoint) => void;
Expand Down Expand Up @@ -54,8 +54,10 @@ export type PackagePolicyResponseExtensionComponent =
ComponentType<PackagePolicyResponseExtensionComponentProps>;

export interface PackagePolicyResponseExtensionComponentProps {
/** The current host id to retrieve response from */
endpointId: string;
/** The current agent to retrieve response from */
agent: Agent;
/** A callback function to set the `needs attention` state */
onShowNeedsAttentionBadge?: (val: boolean) => void;
}

/** Extension point registration contract for Integration Policy Edit views */
Expand Down
Loading