Skip to content

Commit

Permalink
add command upload result to the UI
Browse files Browse the repository at this point in the history
  • Loading branch information
paul-tavares committed May 9, 2023
1 parent 2f9c07f commit 9c436d2
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import type { ActionDetails, MaybeImmutable } from '../../../../common/endpoint/

interface EndpointActionFailureMessageProps {
action: MaybeImmutable<ActionDetails<{ code?: string }>>;
'data-test-subj'?: string;
}

export const EndpointActionFailureMessage = memo<EndpointActionFailureMessageProps>(
({ action }) => {
({ action, 'data-test-subj': dataTestSubj }) => {
return useMemo(() => {
if (!action.isCompleted || action.wasSuccessful) {
return null;
Expand Down Expand Up @@ -55,17 +56,24 @@ export const EndpointActionFailureMessage = memo<EndpointActionFailureMessagePro
}

return (
<>
<div data-test-subj={dataTestSubj}>
<FormattedMessage
id="xpack.securitySolution.endpointResponseActions.actionError.errorMessage"
defaultMessage="The following { errorCount, plural, =1 {error was} other {errors were}} encountered:"
values={{ errorCount: errors.length }}
/>
<EuiSpacer size="s" />
<div>{errors.join(' | ')}</div>
</>
</div>
);
}, [action]);
}, [
action.agents,
action.errors,
action.isCompleted,
action.outputs,
action.wasSuccessful,
dataTestSubj,
]);
}
);
EndpointActionFailureMessage.displayName = 'EndpointActionFailureMessage';
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import React, { memo, useMemo } from 'react';
import { EndpointUploadActionResult } from '../../endpoint_upload_action_result';
import type { UploadActionUIRequestBody } from '../../../../../common/endpoint/schema/actions';
import { useConsoleActionSubmitter } from '../hooks/use_console_action_submitter';
import { useSendUploadEndpointRequest } from '../../../hooks/response_actions/use_send_upload_endpoint_request';
Expand Down Expand Up @@ -53,6 +54,14 @@ export const UploadActionResult = memo<
dataTestSubj: 'upload',
});

return <div>{'UploadActionResult placeholder'}</div>;
if (actionDetails?.isCompleted && actionDetails.wasSuccessful) {
return (
<ResultComponent>
<EndpointUploadActionResult action={actionDetails} />
</ResultComponent>
);
}

return result;
});
UploadActionResult.displayName = 'UploadActionResult';
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { FormattedMessage } from '@kbn/i18n-react';
import React, { memo } from 'react';
import { EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import numeral from '@elastic/numeral';
import { EndpointActionFailureMessage } from '../endpoint_action_failure_message';
import type {
ActionDetails,
ResponseActionUploadOutputContent,
ResponseActionUploadParameters,
} from '../../../../common/endpoint/types';
import { useTestIdGenerator } from '../../hooks/use_test_id_generator';

interface EndpointUploadActionResultProps {
action: ActionDetails<ResponseActionUploadOutputContent, ResponseActionUploadParameters>;
/** The agent id to display the result for. If undefined, the first agent will be used */
agentId?: string;
'data-test-subj'?: string;
}

const LABELS = Object.freeze<Record<string, string>>({
path: i18n.translate('xpack.securitySolution.uploadActionResult.savedTo', {
defaultMessage: 'File saved to',
}),

disk_free_space: i18n.translate('xpack.securitySolution.uploadActionResult.freeDiskSpace', {
defaultMessage: 'Free disk space on drive',
}),

noAgentResponse: i18n.translate('xpack.securitySolution.uploadActionResult.missingAgentResult', {
defaultMessage: 'Error: Agent result missing',
}),
});

export const EndpointUploadActionResult = memo<EndpointUploadActionResultProps>(
({ action, agentId, 'data-test-subj': dataTestSubj }) => {
const getTestId = useTestIdGenerator(dataTestSubj);

const agentState = action?.agentState[agentId ?? action.agents[0]];
const agentResult = action?.outputs?.[agentId ?? action.agents[0]];

if (action.command !== 'upload') {
return null;
}

// Use case: action log
if (!agentState.isCompleted) {
return (
<div data-test-subj={getTestId('pending')}>
<FormattedMessage
id="xpack.securitySolution.uploadActionResult.pendingMessage"
defaultMessage="Action pending."
/>
</div>
);
}

// if we don't have an agent result (for whatever reason)
if (!agentResult) {
return <div data-test-subj={getTestId('noResultError')}>{LABELS.noAgentResponse}</div>;
}

// Error result
if (!agentState.wasSuccessful) {
return (
<EndpointActionFailureMessage
action={action as ActionDetails}
data-test-subj={getTestId('error')}
/>
);
}

return (
<div data-test-subj={getTestId('success')}>
<KeyValueDisplay name={LABELS.path} value={agentResult.content.path} />
<KeyValueDisplay
name={LABELS.disk_free_space}
value={numeral(agentResult.content.disk_free_space).format('0.00b')}
/>
</div>
);
}
);
EndpointUploadActionResult.displayName = 'EndpointUploadActionResult';

export interface KeyValueDisplayProps {
name: string;
value: string;
}
const KeyValueDisplay = memo<KeyValueDisplayProps>(({ name, value }) => {
return (
<EuiText className="eui-textBreakWord" size="s">
<strong>
{name}
{': '}
</strong>
{value}
</EuiText>
);
});
KeyValueDisplay.displayName = 'KeyValueDisplay';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { EndpointUploadActionResult } from './endpoint_upload_action_result';

0 comments on commit 9c436d2

Please sign in to comment.