mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Response Ops][Cases] Quit using legacy API to fetch comments (#203455)
## Summary In order to stop using `includeComments` to load the updated data belonging to the comments/user actions in the cases detail page we implemented a new internal [`find user actions`](https://github.com/elastic/kibana/pull/203455/files#diff-6b8d3c46675fe8f130e37afea148107012bb914a5f82eb277cb2448aba78de29) API. This new API does the same as the public one + an extra step. This extra step is fetching all the attachments by commentId, in here we will have all updates to previous comments, etc. The rest of the PR is updating the case detail page to work with this new schema + test fixing Closes https://github.com/elastic/kibana/issues/194290 --------- Co-authored-by: Christos Nasikas <christos.nasikas@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
4f59641f3a
commit
c3ea94c554
38 changed files with 1831 additions and 292 deletions
|
@ -14,7 +14,7 @@ import {
|
|||
CASE_CONFIGURE_DETAILS_URL,
|
||||
CASE_ALERTS_URL,
|
||||
CASE_COMMENT_DELETE_URL,
|
||||
CASE_FIND_USER_ACTIONS_URL,
|
||||
INTERNAL_CASE_FIND_USER_ACTIONS_URL,
|
||||
INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL,
|
||||
INTERNAL_BULK_GET_ATTACHMENTS_URL,
|
||||
INTERNAL_CONNECTORS_URL,
|
||||
|
@ -57,7 +57,7 @@ export const getCaseUserActionStatsUrl = (id: string): string => {
|
|||
};
|
||||
|
||||
export const getCaseFindUserActionsUrl = (id: string): string => {
|
||||
return CASE_FIND_USER_ACTIONS_URL.replace('{case_id}', id);
|
||||
return INTERNAL_CASE_FIND_USER_ACTIONS_URL.replace('{case_id}', id);
|
||||
};
|
||||
|
||||
export const getCasePushUrl = (caseId: string, connectorId: string): string => {
|
||||
|
|
|
@ -92,6 +92,8 @@ export const INTERNAL_CASE_OBSERVABLES_PATCH_URL =
|
|||
`${INTERNAL_CASE_OBSERVABLES_URL}/{observable_id}` as const;
|
||||
export const INTERNAL_CASE_OBSERVABLES_DELETE_URL =
|
||||
`${INTERNAL_CASE_OBSERVABLES_URL}/{observable_id}` as const;
|
||||
export const INTERNAL_CASE_FIND_USER_ACTIONS_URL =
|
||||
`${CASES_INTERNAL_URL}/{case_id}/user_actions/_find` as const;
|
||||
|
||||
/**
|
||||
* Action routes
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
CaseUserActionBasicRt,
|
||||
UserActionsRt,
|
||||
} from '../../domain/user_action/v1';
|
||||
import type { Attachments } from '../../domain';
|
||||
|
||||
export type UserActionWithResponse<T> = T & { id: string; version: string } & rt.TypeOf<
|
||||
typeof CaseUserActionInjectedIdsRt
|
||||
|
@ -85,3 +86,7 @@ export const UserActionFindResponseRt = rt.strict({
|
|||
});
|
||||
|
||||
export type UserActionFindResponse = rt.TypeOf<typeof UserActionFindResponseRt>;
|
||||
|
||||
export interface UserActionInternalFindResponse extends UserActionFindResponse {
|
||||
latestAttachments: Attachments;
|
||||
}
|
||||
|
|
|
@ -295,6 +295,15 @@ export type PersistableStateAttachmentAttributes = rt.TypeOf<
|
|||
* Common
|
||||
*/
|
||||
|
||||
export const AttachmentPayloadRt = rt.union([
|
||||
UserCommentAttachmentPayloadRt,
|
||||
AlertAttachmentPayloadRt,
|
||||
ActionsAttachmentPayloadRt,
|
||||
ExternalReferenceNoSOAttachmentPayloadRt,
|
||||
ExternalReferenceSOAttachmentPayloadRt,
|
||||
PersistableStateAttachmentPayloadRt,
|
||||
]);
|
||||
|
||||
export const AttachmentAttributesRt = rt.union([
|
||||
UserCommentAttachmentAttributesRt,
|
||||
AlertAttachmentAttributesRt,
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
*/
|
||||
|
||||
import * as rt from 'io-ts';
|
||||
import { AttachmentRequestRt, AttachmentRequestWithoutRefsRt } from '../../../api/attachment/v1';
|
||||
import { AttachmentRequestWithoutRefsRt } from '../../../api/attachment/v1';
|
||||
import { UserActionTypes } from '../action/v1';
|
||||
import { AttachmentPayloadRt } from '../../attachment/v1';
|
||||
|
||||
export const CommentUserActionPayloadRt = rt.strict({ comment: AttachmentPayloadRt });
|
||||
|
||||
export const CommentUserActionPayloadRt = rt.strict({ comment: AttachmentRequestRt });
|
||||
export const CommentUserActionPayloadWithoutIdsRt = rt.strict({
|
||||
comment: AttachmentRequestWithoutRefsRt,
|
||||
});
|
||||
|
|
|
@ -97,6 +97,11 @@ export type UserActionUI = SnakeToCamelCase<UserAction>;
|
|||
export type FindCaseUserActions = Omit<SnakeToCamelCase<UserActionFindResponse>, 'userActions'> & {
|
||||
userActions: UserActionUI[];
|
||||
};
|
||||
|
||||
export interface InternalFindCaseUserActions extends FindCaseUserActions {
|
||||
latestAttachments: AttachmentUI[];
|
||||
}
|
||||
|
||||
export type CaseUserActionsStats = SnakeToCamelCase<CaseUserActionStatsResponse>;
|
||||
export type CaseUI = Omit<SnakeToCamelCase<CaseSnakeCase>, 'comments'> & {
|
||||
comments: AttachmentUI[];
|
||||
|
|
|
@ -38,7 +38,7 @@ import { useGetCaseUserActionsStats } from '../../../containers/use_get_case_use
|
|||
import { useInfiniteFindCaseUserActions } from '../../../containers/use_infinite_find_case_user_actions';
|
||||
import { useOnUpdateField } from '../use_on_update_field';
|
||||
import { useCasesFeatures } from '../../../common/use_cases_features';
|
||||
import { ConnectorTypes, UserActionTypes } from '../../../../common/types/domain';
|
||||
import { AttachmentType, ConnectorTypes, UserActionTypes } from '../../../../common/types/domain';
|
||||
import { CaseMetricsFeature } from '../../../../common/types/api';
|
||||
import { useGetCaseConfiguration } from '../../../containers/configure/use_get_case_configuration';
|
||||
import { useGetCurrentUserProfile } from '../../../containers/user_profiles/use_get_current_user_profile';
|
||||
|
@ -543,6 +543,14 @@ describe('Case View Page activity tab', () => {
|
|||
});
|
||||
|
||||
it('renders the user action users correctly', async () => {
|
||||
const commentUpdate = getUserAction('comment', 'update', {
|
||||
createdBy: {
|
||||
...caseUsers.participants[1].user,
|
||||
fullName: caseUsers.participants[1].user.full_name,
|
||||
profileUid: caseUsers.participants[1].uid,
|
||||
},
|
||||
});
|
||||
|
||||
useFindCaseUserActionsMock.mockReturnValue({
|
||||
...defaultUseFindCaseUserActions,
|
||||
data: {
|
||||
|
@ -555,13 +563,7 @@ describe('Case View Page activity tab', () => {
|
|||
profileUid: caseUsers.participants[0].uid,
|
||||
},
|
||||
}),
|
||||
getUserAction('comment', 'update', {
|
||||
createdBy: {
|
||||
...caseUsers.participants[1].user,
|
||||
fullName: caseUsers.participants[1].user.full_name,
|
||||
profileUid: caseUsers.participants[1].uid,
|
||||
},
|
||||
}),
|
||||
commentUpdate,
|
||||
getUserAction('description', 'update', {
|
||||
createdBy: {
|
||||
...caseUsers.participants[2].user,
|
||||
|
@ -584,6 +586,25 @@ describe('Case View Page activity tab', () => {
|
|||
},
|
||||
}),
|
||||
],
|
||||
latestAttachments:
|
||||
commentUpdate.type === 'comment' &&
|
||||
commentUpdate.payload.comment?.type === AttachmentType.user
|
||||
? [
|
||||
{
|
||||
comment: commentUpdate.payload.comment.comment,
|
||||
createdAt: commentUpdate.createdAt,
|
||||
createdBy: commentUpdate.createdBy,
|
||||
id: commentUpdate.commentId,
|
||||
owner: commentUpdate.owner,
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
type: 'user',
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
version: commentUpdate.version,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
caseUserActions,
|
||||
getAlertUserAction,
|
||||
} from '../../containers/mock';
|
||||
import type { CaseUI } from '../../containers/types';
|
||||
import type { CaseUI, UserActionUI } from '../../containers/types';
|
||||
import type { CaseViewProps } from './types';
|
||||
|
||||
export const alertsHit = [
|
||||
|
@ -99,8 +99,51 @@ export const defaultUpdateCaseState = {
|
|||
mutate: jest.fn(),
|
||||
};
|
||||
|
||||
const generateLatestAttachments = ({
|
||||
userActions,
|
||||
overrides,
|
||||
}: {
|
||||
userActions: UserActionUI[];
|
||||
overrides: Array<{ commentId: string; comment: string }>;
|
||||
}) => {
|
||||
return userActions
|
||||
.filter(
|
||||
(
|
||||
userAction
|
||||
): userAction is UserActionUI & {
|
||||
type: 'comment';
|
||||
payload: { comment: { comment: string } };
|
||||
} => userAction.type === 'comment' && Boolean(userAction.commentId)
|
||||
)
|
||||
.map((userAction) => {
|
||||
const override = overrides.find(({ commentId }) => commentId === userAction.commentId);
|
||||
return {
|
||||
comment: override ? override.comment : userAction.payload.comment?.comment,
|
||||
createdAt: userAction.createdAt,
|
||||
createdBy: userAction.createdBy,
|
||||
id: userAction.commentId,
|
||||
owner: userAction.owner,
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
type: 'user',
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
version: userAction.version,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const defaultUseFindCaseUserActions = {
|
||||
data: { total: 4, perPage: 10, page: 1, userActions: [...caseUserActions, getAlertUserAction()] },
|
||||
data: {
|
||||
total: 4,
|
||||
perPage: 10,
|
||||
page: 1,
|
||||
userActions: [...caseUserActions, getAlertUserAction()],
|
||||
latestAttachments: generateLatestAttachments({
|
||||
userActions: [...caseUserActions, getAlertUserAction()],
|
||||
overrides: [{ commentId: 'basic-comment-id', comment: 'Solve this fast!' }],
|
||||
}),
|
||||
},
|
||||
refetch: jest.fn(),
|
||||
isLoading: false,
|
||||
isFetching: false,
|
||||
|
@ -110,7 +153,13 @@ export const defaultUseFindCaseUserActions = {
|
|||
export const defaultInfiniteUseFindCaseUserActions = {
|
||||
data: {
|
||||
pages: [
|
||||
{ total: 4, perPage: 10, page: 1, userActions: [...caseUserActions, getAlertUserAction()] },
|
||||
{
|
||||
total: 4,
|
||||
perPage: 10,
|
||||
page: 1,
|
||||
userActions: [...caseUserActions, getAlertUserAction()],
|
||||
latestAttachments: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
isLoading: false,
|
||||
|
|
|
@ -21,29 +21,32 @@ type BuilderArgs = Pick<
|
|||
UserActionBuilderArgs,
|
||||
'userAction' | 'actionsNavigation' | 'userProfiles'
|
||||
> & {
|
||||
comment: SnakeToCamelCase<ActionsAttachment>;
|
||||
attachment: SnakeToCamelCase<ActionsAttachment>;
|
||||
};
|
||||
|
||||
export const createActionAttachmentUserActionBuilder = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
actionsNavigation,
|
||||
}: BuilderArgs): ReturnType<UserActionBuilder> => ({
|
||||
build: () => {
|
||||
const actionIconName = comment.actions.type === 'isolate' ? 'lock' : 'lockOpen';
|
||||
const actionIconName = attachment.actions.type === 'isolate' ? 'lock' : 'lockOpen';
|
||||
return [
|
||||
{
|
||||
username: (
|
||||
<HoverableUserWithAvatarResolver user={comment.createdBy} userProfiles={userProfiles} />
|
||||
<HoverableUserWithAvatarResolver
|
||||
user={attachment.createdBy}
|
||||
userProfiles={userProfiles}
|
||||
/>
|
||||
),
|
||||
className: classNames('comment-action', {
|
||||
'empty-comment': comment.comment.trim().length === 0,
|
||||
'empty-comment': attachment.comment.trim().length === 0,
|
||||
}),
|
||||
event: (
|
||||
<HostIsolationCommentEvent
|
||||
type={comment.actions.type}
|
||||
endpoints={comment.actions.targets}
|
||||
type={attachment.actions.type}
|
||||
endpoints={attachment.actions.targets}
|
||||
href={actionsNavigation?.href}
|
||||
onClick={actionsNavigation?.onClick}
|
||||
/>
|
||||
|
@ -52,9 +55,9 @@ export const createActionAttachmentUserActionBuilder = ({
|
|||
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
|
||||
timelineAvatar: actionIconName,
|
||||
timelineAvatarAriaLabel: actionIconName,
|
||||
actions: <UserActionCopyLink id={comment.id} />,
|
||||
children: comment.comment.trim().length > 0 && (
|
||||
<ScrollableMarkdown content={comment.comment} />
|
||||
actions: <UserActionCopyLink id={attachment.id} />,
|
||||
children: attachment.comment.trim().length > 0 && (
|
||||
<ScrollableMarkdown content={attachment.comment} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
|
@ -34,12 +34,12 @@ type BuilderArgs = Pick<
|
|||
| 'userProfiles'
|
||||
| 'handleDeleteComment'
|
||||
| 'loadingCommentIds'
|
||||
> & { comment: SnakeToCamelCase<AlertAttachment> };
|
||||
> & { attachment: SnakeToCamelCase<AlertAttachment> };
|
||||
|
||||
const getSingleAlertUserAction = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
alertData,
|
||||
loadingAlertData,
|
||||
loadingCommentIds,
|
||||
|
@ -48,16 +48,16 @@ const getSingleAlertUserAction = ({
|
|||
onShowAlertDetails,
|
||||
handleDeleteComment,
|
||||
}: BuilderArgs): EuiCommentProps[] => {
|
||||
const alertId = getNonEmptyField(comment.alertId);
|
||||
const alertIndex = getNonEmptyField(comment.index);
|
||||
const alertId = getNonEmptyField(attachment.alertId);
|
||||
const alertIndex = getNonEmptyField(attachment.index);
|
||||
|
||||
if (!alertId || !alertIndex) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const alertField: unknown | undefined = alertData[alertId];
|
||||
const ruleId = getRuleId(comment, alertField);
|
||||
const ruleName = getRuleName(comment, alertField);
|
||||
const ruleId = getRuleId(attachment, alertField);
|
||||
const ruleName = getRuleName(attachment, alertField);
|
||||
|
||||
return [
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ const getSingleAlertUserAction = ({
|
|||
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
|
||||
timelineAvatar: 'bell',
|
||||
actions: (
|
||||
<UserActionContentToolbar id={comment.id}>
|
||||
<UserActionContentToolbar id={attachment.id}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<UserActionShowAlert
|
||||
id={userAction.id}
|
||||
|
@ -89,8 +89,8 @@ const getSingleAlertUserAction = ({
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<AlertPropertyActions
|
||||
onDelete={() => handleDeleteComment(comment.id, DELETE_ALERTS_SUCCESS_TITLE(1))}
|
||||
isLoading={loadingCommentIds.includes(comment.id)}
|
||||
onDelete={() => handleDeleteComment(attachment.id, DELETE_ALERTS_SUCCESS_TITLE(1))}
|
||||
isLoading={loadingCommentIds.includes(attachment.id)}
|
||||
totalAlerts={1}
|
||||
/>
|
||||
</UserActionContentToolbar>
|
||||
|
@ -102,7 +102,7 @@ const getSingleAlertUserAction = ({
|
|||
const getMultipleAlertsUserAction = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
alertData,
|
||||
loadingAlertData,
|
||||
loadingCommentIds,
|
||||
|
@ -110,12 +110,12 @@ const getMultipleAlertsUserAction = ({
|
|||
onRuleDetailsClick,
|
||||
handleDeleteComment,
|
||||
}: BuilderArgs): EuiCommentProps[] => {
|
||||
if (!Array.isArray(comment.alertId)) {
|
||||
if (!Array.isArray(attachment.alertId)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const totalAlerts = comment.alertId.length;
|
||||
const { ruleId, ruleName } = getRuleInfo(comment, alertData);
|
||||
const totalAlerts = attachment.alertId.length;
|
||||
const { ruleId, ruleName } = getRuleInfo(attachment, alertData);
|
||||
|
||||
return [
|
||||
{
|
||||
|
@ -138,15 +138,15 @@ const getMultipleAlertsUserAction = ({
|
|||
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
|
||||
timelineAvatar: 'bell',
|
||||
actions: (
|
||||
<UserActionContentToolbar id={comment.id}>
|
||||
<UserActionContentToolbar id={attachment.id}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<ShowAlertTableLink />
|
||||
</EuiFlexItem>
|
||||
<AlertPropertyActions
|
||||
onDelete={() =>
|
||||
handleDeleteComment(comment.id, DELETE_ALERTS_SUCCESS_TITLE(totalAlerts))
|
||||
handleDeleteComment(attachment.id, DELETE_ALERTS_SUCCESS_TITLE(totalAlerts))
|
||||
}
|
||||
isLoading={loadingCommentIds.includes(comment.id)}
|
||||
isLoading={loadingCommentIds.includes(attachment.id)}
|
||||
totalAlerts={totalAlerts}
|
||||
/>
|
||||
</UserActionContentToolbar>
|
||||
|
@ -159,8 +159,8 @@ export const createAlertAttachmentUserActionBuilder = (
|
|||
params: BuilderArgs
|
||||
): ReturnType<UserActionBuilder> => ({
|
||||
build: () => {
|
||||
const { comment } = params;
|
||||
const alertId = Array.isArray(comment.alertId) ? comment.alertId : [comment.alertId];
|
||||
const { attachment } = params;
|
||||
const alertId = Array.isArray(attachment.alertId) ? attachment.alertId : [attachment.alertId];
|
||||
|
||||
if (alertId.length === 1) {
|
||||
return getSingleAlertUserAction(params);
|
||||
|
@ -174,35 +174,41 @@ const getFirstItem = (items?: string | string[] | null): string | null => {
|
|||
return Array.isArray(items) ? items[0] : items ?? null;
|
||||
};
|
||||
|
||||
export const getRuleId = (comment: BuilderArgs['comment'], alertData?: unknown): string | null =>
|
||||
export const getRuleId = (
|
||||
attachment: BuilderArgs['attachment'],
|
||||
alertData?: unknown
|
||||
): string | null =>
|
||||
getRuleField({
|
||||
commentRuleField: comment?.rule?.id,
|
||||
attachmentRuleField: attachment?.rule?.id,
|
||||
alertData,
|
||||
signalRuleFieldPath: 'signal.rule.id',
|
||||
kibanaAlertFieldPath: ALERT_RULE_UUID,
|
||||
});
|
||||
|
||||
export const getRuleName = (comment: BuilderArgs['comment'], alertData?: unknown): string | null =>
|
||||
export const getRuleName = (
|
||||
attachment: BuilderArgs['attachment'],
|
||||
alertData?: unknown
|
||||
): string | null =>
|
||||
getRuleField({
|
||||
commentRuleField: comment?.rule?.name,
|
||||
attachmentRuleField: attachment?.rule?.name,
|
||||
alertData,
|
||||
signalRuleFieldPath: 'signal.rule.name',
|
||||
kibanaAlertFieldPath: ALERT_RULE_NAME,
|
||||
});
|
||||
|
||||
const getRuleField = ({
|
||||
commentRuleField,
|
||||
attachmentRuleField,
|
||||
alertData,
|
||||
signalRuleFieldPath,
|
||||
kibanaAlertFieldPath,
|
||||
}: {
|
||||
commentRuleField: string | string[] | null | undefined;
|
||||
attachmentRuleField: string | string[] | null | undefined;
|
||||
alertData: unknown | undefined;
|
||||
signalRuleFieldPath: string;
|
||||
kibanaAlertFieldPath: string;
|
||||
}): string | null => {
|
||||
const field =
|
||||
getNonEmptyField(commentRuleField) ??
|
||||
getNonEmptyField(attachmentRuleField) ??
|
||||
getNonEmptyField(get(alertData, signalRuleFieldPath)) ??
|
||||
getNonEmptyField(get(alertData, kibanaAlertFieldPath));
|
||||
|
||||
|
@ -218,16 +224,19 @@ function getNonEmptyField(field: string | string[] | undefined | null): string |
|
|||
return firstItem;
|
||||
}
|
||||
|
||||
export function getRuleInfo(comment: BuilderArgs['comment'], alertData: BuilderArgs['alertData']) {
|
||||
const alertId = getNonEmptyField(comment.alertId);
|
||||
export function getRuleInfo(
|
||||
attachment: BuilderArgs['attachment'],
|
||||
alertData: BuilderArgs['alertData']
|
||||
) {
|
||||
const alertId = getNonEmptyField(attachment.alertId);
|
||||
|
||||
if (!alertId) {
|
||||
return { ruleId: null, ruleName: null };
|
||||
}
|
||||
|
||||
const alertField: unknown | undefined = alertData[alertId];
|
||||
const ruleId = getRuleId(comment, alertField);
|
||||
const ruleName = getRuleName(comment, alertField);
|
||||
const ruleId = getRuleId(attachment, alertField);
|
||||
const ruleName = getRuleName(attachment, alertField);
|
||||
|
||||
return { ruleId, ruleName };
|
||||
}
|
||||
|
|
|
@ -408,8 +408,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [alertComment],
|
||||
},
|
||||
attachments: [alertComment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -432,8 +432,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [alertComment],
|
||||
},
|
||||
attachments: [alertComment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -465,8 +465,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [alertComment],
|
||||
},
|
||||
attachments: [alertComment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -501,14 +501,14 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -528,14 +528,14 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -564,14 +564,14 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
...alertComment,
|
||||
alertId: ['alert-id-1', 'alert-id-2'],
|
||||
index: ['alert-index-1', 'alert-index-2'],
|
||||
},
|
||||
],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -595,8 +595,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [hostIsolationComment()],
|
||||
},
|
||||
attachments: [hostIsolationComment()],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -623,8 +623,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
...builderArgs,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [hostIsolationComment({ createdBy })],
|
||||
},
|
||||
attachments: [hostIsolationComment({ createdBy })],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -663,17 +663,17 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [
|
||||
{
|
||||
...externalReferenceAttachment,
|
||||
createdBy: {
|
||||
username: damagedRaccoon.user.username,
|
||||
fullName: damagedRaccoon.user.full_name,
|
||||
email: damagedRaccoon.user.email,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
attachments: [
|
||||
{
|
||||
...externalReferenceAttachment,
|
||||
createdBy: {
|
||||
username: damagedRaccoon.user.username,
|
||||
fullName: damagedRaccoon.user.full_name,
|
||||
email: damagedRaccoon.user.email,
|
||||
},
|
||||
},
|
||||
],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -696,8 +696,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -720,8 +720,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -780,8 +780,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment01],
|
||||
},
|
||||
attachments: [attachment01],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -809,8 +809,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [attachment02],
|
||||
},
|
||||
attachments: [attachment02],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -832,8 +832,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
attachments: [persistableStateAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -856,8 +856,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [persistableStateAttachment],
|
||||
},
|
||||
attachments: [persistableStateAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -915,8 +915,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -962,8 +962,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -1018,8 +1018,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -1073,8 +1073,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -1149,8 +1149,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
@ -1200,8 +1200,8 @@ describe('createCommentUserActionBuilder', () => {
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData: {
|
||||
...builderArgs.caseData,
|
||||
comments: [externalReferenceAttachment],
|
||||
},
|
||||
attachments: [externalReferenceAttachment],
|
||||
userAction,
|
||||
});
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ const getCreateCommentUserAction = ({
|
|||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
comment,
|
||||
attachment,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -166,21 +166,21 @@ const getCreateCommentUserAction = ({
|
|||
actionsNavigation,
|
||||
}: {
|
||||
userAction: SnakeToCamelCase<CommentUserAction>;
|
||||
comment: AttachmentUI;
|
||||
attachment: AttachmentUI;
|
||||
} & Omit<
|
||||
UserActionBuilderArgs,
|
||||
'comments' | 'index' | 'handleOutlineComment' | 'currentUserProfile'
|
||||
>): EuiCommentProps[] => {
|
||||
switch (comment.type) {
|
||||
switch (attachment.type) {
|
||||
case AttachmentType.user:
|
||||
const userBuilder = createUserAttachmentUserActionBuilder({
|
||||
appId,
|
||||
userProfiles,
|
||||
comment,
|
||||
outlined: comment.id === selectedOutlineCommentId,
|
||||
isEdit: manageMarkdownEditIds.includes(comment.id),
|
||||
attachment,
|
||||
outlined: attachment.id === selectedOutlineCommentId,
|
||||
isEdit: manageMarkdownEditIds.includes(attachment.id),
|
||||
commentRefs,
|
||||
isLoading: loadingCommentIds.includes(comment.id),
|
||||
isLoading: loadingCommentIds.includes(attachment.id),
|
||||
caseId: caseData.id,
|
||||
euiTheme,
|
||||
handleManageMarkdownEditId,
|
||||
|
@ -195,7 +195,7 @@ const getCreateCommentUserAction = ({
|
|||
const alertBuilder = createAlertAttachmentUserActionBuilder({
|
||||
userProfiles,
|
||||
alertData,
|
||||
comment,
|
||||
attachment,
|
||||
userAction,
|
||||
getRuleDetailsHref,
|
||||
loadingAlertData,
|
||||
|
@ -211,7 +211,7 @@ const getCreateCommentUserAction = ({
|
|||
const actionBuilder = createActionAttachmentUserActionBuilder({
|
||||
userProfiles,
|
||||
userAction,
|
||||
comment,
|
||||
attachment,
|
||||
actionsNavigation,
|
||||
});
|
||||
|
||||
|
@ -221,10 +221,10 @@ const getCreateCommentUserAction = ({
|
|||
const externalReferenceBuilder = createExternalReferenceAttachmentUserActionBuilder({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData,
|
||||
isLoading: loadingCommentIds.includes(comment.id),
|
||||
isLoading: loadingCommentIds.includes(attachment.id),
|
||||
handleDeleteComment,
|
||||
});
|
||||
|
||||
|
@ -234,10 +234,10 @@ const getCreateCommentUserAction = ({
|
|||
const persistableBuilder = createPersistableStateAttachmentUserActionBuilder({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData,
|
||||
isLoading: loadingCommentIds.includes(comment.id),
|
||||
isLoading: loadingCommentIds.includes(attachment.id),
|
||||
handleDeleteComment,
|
||||
});
|
||||
|
||||
|
@ -273,13 +273,14 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
handleOutlineComment,
|
||||
actionsNavigation,
|
||||
caseConnectors,
|
||||
attachments,
|
||||
}) => ({
|
||||
build: () => {
|
||||
const commentUserAction = userAction as SnakeToCamelCase<CommentUserAction>;
|
||||
const attachmentUserAction = userAction as SnakeToCamelCase<CommentUserAction>;
|
||||
|
||||
if (commentUserAction.action === UserActionActions.delete) {
|
||||
if (attachmentUserAction.action === UserActionActions.delete) {
|
||||
return getDeleteCommentUserAction({
|
||||
userAction: commentUserAction,
|
||||
userAction: attachmentUserAction,
|
||||
caseData,
|
||||
handleOutlineComment,
|
||||
userProfiles,
|
||||
|
@ -288,22 +289,22 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
});
|
||||
}
|
||||
|
||||
const comment = caseData.comments.find((c) => c.id === commentUserAction.commentId);
|
||||
const attachment = attachments.find((c) => c.id === attachmentUserAction.commentId);
|
||||
|
||||
if (comment == null) {
|
||||
if (attachment == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (commentUserAction.action === UserActionActions.create) {
|
||||
if (attachmentUserAction.action === UserActionActions.create) {
|
||||
const commentAction = getCreateCommentUserAction({
|
||||
appId,
|
||||
caseData,
|
||||
casesConfiguration,
|
||||
userProfiles,
|
||||
userAction: commentUserAction,
|
||||
userAction: attachmentUserAction,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
comment,
|
||||
attachment,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -320,6 +321,7 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
handleManageQuote,
|
||||
actionsNavigation,
|
||||
caseConnectors,
|
||||
attachments,
|
||||
});
|
||||
|
||||
return commentAction;
|
||||
|
|
|
@ -18,14 +18,14 @@ type BuilderArgs = Pick<
|
|||
| 'handleDeleteComment'
|
||||
| 'userProfiles'
|
||||
> & {
|
||||
comment: SnakeToCamelCase<ExternalReferenceAttachment>;
|
||||
attachment: SnakeToCamelCase<ExternalReferenceAttachment>;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
export const createExternalReferenceAttachmentUserActionBuilder = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
caseData,
|
||||
isLoading,
|
||||
|
@ -34,15 +34,15 @@ export const createExternalReferenceAttachmentUserActionBuilder = ({
|
|||
return createRegisteredAttachmentUserActionBuilder({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
registry: externalReferenceAttachmentTypeRegistry,
|
||||
caseData,
|
||||
handleDeleteComment,
|
||||
isLoading,
|
||||
getId: () => comment.externalReferenceAttachmentTypeId,
|
||||
getId: () => attachment.externalReferenceAttachmentTypeId,
|
||||
getAttachmentViewProps: () => ({
|
||||
externalReferenceId: comment.externalReferenceId,
|
||||
externalReferenceMetadata: comment.externalReferenceMetadata,
|
||||
externalReferenceId: attachment.externalReferenceId,
|
||||
externalReferenceMetadata: attachment.externalReferenceMetadata,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -18,14 +18,14 @@ type BuilderArgs = Pick<
|
|||
| 'handleDeleteComment'
|
||||
| 'userProfiles'
|
||||
> & {
|
||||
comment: SnakeToCamelCase<PersistableStateAttachment>;
|
||||
attachment: SnakeToCamelCase<PersistableStateAttachment>;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
export const createPersistableStateAttachmentUserActionBuilder = ({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
persistableStateAttachmentTypeRegistry,
|
||||
caseData,
|
||||
isLoading,
|
||||
|
@ -34,15 +34,15 @@ export const createPersistableStateAttachmentUserActionBuilder = ({
|
|||
return createRegisteredAttachmentUserActionBuilder({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
registry: persistableStateAttachmentTypeRegistry,
|
||||
caseData,
|
||||
handleDeleteComment,
|
||||
isLoading,
|
||||
getId: () => comment.persistableStateAttachmentTypeId,
|
||||
getId: () => attachment.persistableStateAttachmentTypeId,
|
||||
getAttachmentViewProps: () => ({
|
||||
persistableStateAttachmentTypeId: comment.persistableStateAttachmentTypeId,
|
||||
persistableStateAttachmentState: comment.persistableStateAttachmentState,
|
||||
persistableStateAttachmentTypeId: attachment.persistableStateAttachmentTypeId,
|
||||
persistableStateAttachmentState: attachment.persistableStateAttachmentState,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@ describe('createRegisteredAttachmentUserActionBuilder', () => {
|
|||
|
||||
registry.register(item);
|
||||
|
||||
const comment = builderArgs.comments[0];
|
||||
const attachment = builderArgs.attachments[0];
|
||||
|
||||
const userActionBuilderArgs = {
|
||||
userAction: builderArgs.userAction,
|
||||
|
@ -68,7 +68,7 @@ describe('createRegisteredAttachmentUserActionBuilder', () => {
|
|||
getId,
|
||||
getAttachmentViewProps,
|
||||
isLoading: false,
|
||||
comment,
|
||||
attachment,
|
||||
registry,
|
||||
};
|
||||
|
||||
|
@ -97,7 +97,7 @@ describe('createRegisteredAttachmentUserActionBuilder', () => {
|
|||
expect(getAttachmentViewProps).toHaveBeenCalled();
|
||||
expect(getAttachmentViewObject).toBeCalledWith({
|
||||
...viewProps,
|
||||
attachmentId: comment.id,
|
||||
attachmentId: attachment.id,
|
||||
caseData: { id: builderArgs.caseData.id, title: builderArgs.caseData.title },
|
||||
});
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ type BuilderArgs<C, R> = Pick<
|
|||
UserActionBuilderArgs,
|
||||
'userAction' | 'caseData' | 'handleDeleteComment' | 'userProfiles'
|
||||
> & {
|
||||
comment: SnakeToCamelCase<C>;
|
||||
attachment: SnakeToCamelCase<C>;
|
||||
registry: R;
|
||||
isLoading: boolean;
|
||||
getId: () => string;
|
||||
|
@ -82,7 +82,7 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
>({
|
||||
userAction,
|
||||
userProfiles,
|
||||
comment,
|
||||
attachment,
|
||||
registry,
|
||||
caseData,
|
||||
isLoading,
|
||||
|
@ -98,7 +98,10 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
return [
|
||||
{
|
||||
username: (
|
||||
<HoverableUserWithAvatarResolver user={comment.createdBy} userProfiles={userProfiles} />
|
||||
<HoverableUserWithAvatarResolver
|
||||
user={attachment.createdBy}
|
||||
userProfiles={userProfiles}
|
||||
/>
|
||||
),
|
||||
event: (
|
||||
<>
|
||||
|
@ -106,8 +109,8 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
<EuiCode>{attachmentTypeId}</EuiCode>
|
||||
</>
|
||||
),
|
||||
className: `comment-${comment.type}-not-found`,
|
||||
'data-test-subj': `comment-${comment.type}-not-found`,
|
||||
className: `comment-${attachment.type}-not-found`,
|
||||
'data-test-subj': `comment-${attachment.type}-not-found`,
|
||||
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
|
||||
children: (
|
||||
<EuiCallOut title={ATTACHMENT_NOT_REGISTERED_ERROR} color="danger" iconType="warning" />
|
||||
|
@ -120,7 +123,7 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
|
||||
const props = {
|
||||
...getAttachmentViewProps(),
|
||||
attachmentId: comment.id,
|
||||
attachmentId: attachment.id,
|
||||
caseData: { id: caseData.id, title: caseData.title },
|
||||
};
|
||||
|
||||
|
@ -135,30 +138,33 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
return [
|
||||
{
|
||||
username: (
|
||||
<HoverableUserWithAvatarResolver user={comment.createdBy} userProfiles={userProfiles} />
|
||||
<HoverableUserWithAvatarResolver
|
||||
user={attachment.createdBy}
|
||||
userProfiles={userProfiles}
|
||||
/>
|
||||
),
|
||||
className: `comment-${comment.type}-attachment-${attachmentTypeId}`,
|
||||
className: `comment-${attachment.type}-attachment-${attachmentTypeId}`,
|
||||
event: attachmentViewObject.event,
|
||||
'data-test-subj': `comment-${comment.type}-${attachmentTypeId}`,
|
||||
'data-test-subj': `comment-${attachment.type}-${attachmentTypeId}`,
|
||||
timestamp: <UserActionTimestamp createdAt={userAction.createdAt} />,
|
||||
timelineAvatar: attachmentViewObject.timelineAvatar,
|
||||
actions: (
|
||||
<UserActionContentToolbar id={comment.id}>
|
||||
<UserActionContentToolbar id={attachment.id}>
|
||||
{visiblePrimaryActions.map(
|
||||
(action) =>
|
||||
(action.type === AttachmentActionType.BUTTON && (
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${comment.id}`}
|
||||
key={`attachment-${attachmentTypeId}-${comment.id}`}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${attachment.id}`}
|
||||
key={`attachment-${attachmentTypeId}-${attachment.id}`}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
aria-label={action.label}
|
||||
iconType={action.iconType}
|
||||
color={action.color ?? 'text'}
|
||||
onClick={action.onClick}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${comment.id}-${action.iconType}`}
|
||||
key={`attachment-${attachmentTypeId}-${comment.id}-${action.iconType}`}
|
||||
data-test-subj={`attachment-${attachmentTypeId}-${attachment.id}-${action.iconType}`}
|
||||
key={`attachment-${attachmentTypeId}-${attachment.id}-${action.iconType}`}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)) ||
|
||||
|
@ -166,7 +172,7 @@ export const createRegisteredAttachmentUserActionBuilder = <
|
|||
)}
|
||||
<RegisteredAttachmentsPropertyActions
|
||||
isLoading={isLoading}
|
||||
onDelete={() => handleDeleteComment(comment.id, DELETE_REGISTERED_ATTACHMENT)}
|
||||
onDelete={() => handleDeleteComment(attachment.id, DELETE_REGISTERED_ATTACHMENT)}
|
||||
registeredAttachmentActions={[...nonVisiblePrimaryActions, ...nonPrimaryActions]}
|
||||
hideDefaultActions={!!attachmentViewObject.hideDefaultActions}
|
||||
/>
|
||||
|
|
|
@ -34,7 +34,7 @@ type BuilderArgs = Pick<
|
|||
| 'appId'
|
||||
| 'euiTheme'
|
||||
> & {
|
||||
comment: SnakeToCamelCase<UserCommentAttachment>;
|
||||
attachment: SnakeToCamelCase<UserCommentAttachment>;
|
||||
caseId: string;
|
||||
outlined: boolean;
|
||||
isEdit: boolean;
|
||||
|
@ -66,7 +66,7 @@ const hasDraftComment = (
|
|||
|
||||
export const createUserAttachmentUserActionBuilder = ({
|
||||
appId,
|
||||
comment,
|
||||
attachment,
|
||||
userProfiles,
|
||||
outlined,
|
||||
isEdit,
|
||||
|
@ -81,33 +81,39 @@ export const createUserAttachmentUserActionBuilder = ({
|
|||
}: BuilderArgs): ReturnType<UserActionBuilder> => ({
|
||||
build: () => [
|
||||
{
|
||||
username: <HoverableUsernameResolver user={comment.createdBy} userProfiles={userProfiles} />,
|
||||
'data-test-subj': `comment-create-action-${comment.id}`,
|
||||
username: (
|
||||
<HoverableUsernameResolver user={attachment.createdBy} userProfiles={userProfiles} />
|
||||
),
|
||||
'data-test-subj': `comment-create-action-${attachment.id}`,
|
||||
timestamp: (
|
||||
<UserActionTimestamp createdAt={comment.createdAt} updatedAt={comment.updatedAt} />
|
||||
<UserActionTimestamp createdAt={attachment.createdAt} updatedAt={attachment.updatedAt} />
|
||||
),
|
||||
className: classNames('userAction__comment', {
|
||||
outlined,
|
||||
isEdit,
|
||||
draftFooter:
|
||||
!isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id, comment.comment),
|
||||
!isEdit &&
|
||||
!isLoading &&
|
||||
hasDraftComment(appId, caseId, attachment.id, attachment.comment),
|
||||
}),
|
||||
children: (
|
||||
<>
|
||||
<UserActionMarkdown
|
||||
key={isEdit ? comment.id : undefined}
|
||||
ref={(element) => (commentRefs.current[comment.id] = element)}
|
||||
id={comment.id}
|
||||
content={comment.comment}
|
||||
key={isEdit ? attachment.id : undefined}
|
||||
ref={(element) => (commentRefs.current[attachment.id] = element)}
|
||||
id={attachment.id}
|
||||
content={attachment.comment}
|
||||
isEditable={isEdit}
|
||||
caseId={caseId}
|
||||
onChangeEditable={handleManageMarkdownEditId}
|
||||
onSaveContent={handleSaveComment.bind(null, {
|
||||
id: comment.id,
|
||||
version: comment.version,
|
||||
id: attachment.id,
|
||||
version: attachment.version,
|
||||
})}
|
||||
/>
|
||||
{!isEdit && !isLoading && hasDraftComment(appId, caseId, comment.id, comment.comment) ? (
|
||||
{!isEdit &&
|
||||
!isLoading &&
|
||||
hasDraftComment(appId, caseId, attachment.id, attachment.comment) ? (
|
||||
<EuiText css={getCommentFooterCss(euiTheme)}>
|
||||
<EuiText color="subdued" size="xs" data-test-subj="user-action-comment-unsaved-draft">
|
||||
{i18n.UNSAVED_DRAFT_COMMENT}
|
||||
|
@ -119,16 +125,16 @@ export const createUserAttachmentUserActionBuilder = ({
|
|||
</>
|
||||
),
|
||||
timelineAvatar: (
|
||||
<HoverableAvatarResolver user={comment.createdBy} userProfiles={userProfiles} />
|
||||
<HoverableAvatarResolver user={attachment.createdBy} userProfiles={userProfiles} />
|
||||
),
|
||||
actions: (
|
||||
<UserActionContentToolbar id={comment.id}>
|
||||
<UserActionContentToolbar id={attachment.id}>
|
||||
<UserCommentPropertyActions
|
||||
isLoading={isLoading}
|
||||
commentContent={comment.comment}
|
||||
onEdit={() => handleManageMarkdownEditId(comment.id)}
|
||||
onDelete={() => handleDeleteComment(comment.id, i18n.DELETE_COMMENT_SUCCESS_TITLE)}
|
||||
onQuote={() => handleManageQuote(comment.comment)}
|
||||
commentContent={attachment.comment}
|
||||
onEdit={() => handleManageMarkdownEditId(attachment.id)}
|
||||
onDelete={() => handleDeleteComment(attachment.id, i18n.DELETE_COMMENT_SUCCESS_TITLE)}
|
||||
onQuote={() => handleManageQuote(attachment.comment)}
|
||||
/>
|
||||
</UserActionContentToolbar>
|
||||
),
|
||||
|
|
|
@ -82,6 +82,7 @@ export const UserActions = React.memo((props: UserActionTreeProps) => {
|
|||
|
||||
const {
|
||||
infiniteCaseUserActions,
|
||||
infiniteLatestAttachments,
|
||||
isLoadingInfiniteUserActions,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
|
@ -95,11 +96,12 @@ export const UserActions = React.memo((props: UserActionTreeProps) => {
|
|||
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
const { isLoadingLastPageUserActions, lastPageUserActions } = useLastPageUserActions({
|
||||
userActivityQueryParams,
|
||||
caseId: caseData.id,
|
||||
lastPage,
|
||||
});
|
||||
const { isLoadingLastPageUserActions, lastPageUserActions, lastPageAttachments } =
|
||||
useLastPageUserActions({
|
||||
userActivityQueryParams,
|
||||
caseId: caseData.id,
|
||||
lastPage,
|
||||
});
|
||||
|
||||
const alertIdsWithoutRuleInfo = useMemo(
|
||||
() => getManualAlertIdsWithNoRuleId(caseData.comments),
|
||||
|
@ -180,6 +182,7 @@ export const UserActions = React.memo((props: UserActionTreeProps) => {
|
|||
<UserActionsList
|
||||
{...props}
|
||||
caseUserActions={infiniteCaseUserActions}
|
||||
attachments={infiniteLatestAttachments}
|
||||
loadingAlertData={loadingAlertData}
|
||||
manualAlertsData={manualAlertsData}
|
||||
commentRefs={commentRefs}
|
||||
|
@ -203,6 +206,7 @@ export const UserActions = React.memo((props: UserActionTreeProps) => {
|
|||
<UserActionsList
|
||||
{...props}
|
||||
caseUserActions={lastPageUserActions}
|
||||
attachments={lastPageAttachments}
|
||||
loadingAlertData={loadingAlertData}
|
||||
manualAlertsData={manualAlertsData}
|
||||
bottomActions={bottomActions}
|
||||
|
|
|
@ -65,7 +65,7 @@ export const getMockBuilderArgs = (): UserActionBuilderArgs => {
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
caseData: basicCase,
|
||||
casesConfiguration: casesConfigurationsMock,
|
||||
comments: basicCase.comments,
|
||||
attachments: basicCase.comments,
|
||||
index: 0,
|
||||
alertData,
|
||||
commentRefs,
|
||||
|
|
|
@ -60,7 +60,7 @@ export interface UserActionBuilderArgs {
|
|||
persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry;
|
||||
caseConnectors: CaseConnectors;
|
||||
userAction: UserActionUI;
|
||||
comments: AttachmentUI[];
|
||||
attachments: AttachmentUI[];
|
||||
index: number;
|
||||
commentRefs: React.MutableRefObject<
|
||||
Record<string, AddCommentRefObject | UserActionMarkdownRefObject | null | undefined>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import type { UserActionUI } from '../../containers/types';
|
||||
import type { AttachmentUI, UserActionUI } from '../../containers/types';
|
||||
import { useFindCaseUserActions } from '../../containers/use_find_case_user_actions';
|
||||
import type { UserActivityParams } from '../user_actions_activity_bar/types';
|
||||
|
||||
|
@ -25,16 +25,23 @@ export const useLastPageUserActions = ({
|
|||
const { data: lastPageUserActionsData, isLoading: isLoadingLastPageUserActions } =
|
||||
useFindCaseUserActions(caseId, { ...userActivityQueryParams, page: lastPage }, lastPage > 1);
|
||||
|
||||
const lastPageUserActions = useMemo<UserActionUI[]>(() => {
|
||||
const { userActions, latestAttachments } = useMemo<{
|
||||
userActions: UserActionUI[];
|
||||
latestAttachments: AttachmentUI[];
|
||||
}>(() => {
|
||||
if (isLoadingLastPageUserActions || !lastPageUserActionsData) {
|
||||
return [];
|
||||
return { userActions: [], latestAttachments: [] };
|
||||
}
|
||||
|
||||
return lastPageUserActionsData.userActions;
|
||||
return {
|
||||
userActions: lastPageUserActionsData.userActions,
|
||||
latestAttachments: lastPageUserActionsData.latestAttachments,
|
||||
};
|
||||
}, [lastPageUserActionsData, isLoadingLastPageUserActions]);
|
||||
|
||||
return {
|
||||
isLoadingLastPageUserActions,
|
||||
lastPageUserActions,
|
||||
lastPageUserActions: userActions,
|
||||
lastPageAttachments: latestAttachments,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { useMemo } from 'react';
|
||||
|
||||
import { useInfiniteFindCaseUserActions } from '../../containers/use_infinite_find_case_user_actions';
|
||||
import type { UserActionUI } from '../../containers/types';
|
||||
import type { AttachmentUI, UserActionUI } from '../../containers/types';
|
||||
import type { UserActivityParams } from '../user_actions_activity_bar/types';
|
||||
|
||||
interface UserActionsPagination {
|
||||
|
@ -32,23 +32,32 @@ export const useUserActionsPagination = ({
|
|||
|
||||
const showBottomList = lastPage > 1;
|
||||
|
||||
const infiniteCaseUserActions = useMemo<UserActionUI[]>(() => {
|
||||
const infiniteCaseUserActions = useMemo<{
|
||||
userActions: UserActionUI[];
|
||||
latestAttachments: AttachmentUI[];
|
||||
}>(() => {
|
||||
if (!caseInfiniteUserActionsData?.pages?.length || isLoadingInfiniteUserActions) {
|
||||
return [];
|
||||
return { userActions: [], latestAttachments: [] };
|
||||
}
|
||||
|
||||
const userActionsData: UserActionUI[] = [];
|
||||
const latestAttachments: AttachmentUI[] = [];
|
||||
|
||||
// TODO: looks like it can be done in one loop
|
||||
caseInfiniteUserActionsData.pages.forEach((page) => userActionsData.push(...page.userActions));
|
||||
caseInfiniteUserActionsData.pages.forEach((page) =>
|
||||
latestAttachments.push(...page.latestAttachments)
|
||||
);
|
||||
|
||||
return userActionsData;
|
||||
return { userActions: userActionsData, latestAttachments };
|
||||
}, [caseInfiniteUserActionsData, isLoadingInfiniteUserActions]);
|
||||
|
||||
return {
|
||||
lastPage,
|
||||
showBottomList,
|
||||
isLoadingInfiniteUserActions,
|
||||
infiniteCaseUserActions,
|
||||
infiniteCaseUserActions: infiniteCaseUserActions.userActions,
|
||||
infiniteLatestAttachments: infiniteCaseUserActions.latestAttachments,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage,
|
||||
|
|
|
@ -11,7 +11,7 @@ import { EuiCommentList, useEuiTheme } from '@elastic/eui';
|
|||
import React, { useMemo, useEffect, useState } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
import type { UserActionUI } from '../../containers/types';
|
||||
import type { AttachmentUI, UserActionUI } from '../../containers/types';
|
||||
import type { UserActionBuilderArgs, UserActionTreeProps } from './types';
|
||||
import { isUserActionTypeSupported } from './helpers';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
@ -66,6 +66,7 @@ export type UserActionListProps = Omit<
|
|||
> &
|
||||
Pick<UserActionBuilderArgs, 'commentRefs' | 'handleManageQuote'> & {
|
||||
caseUserActions: UserActionUI[];
|
||||
attachments: AttachmentUI[];
|
||||
loadingAlertData: boolean;
|
||||
manualAlertsData: Record<string, unknown>;
|
||||
bottomActions?: EuiCommentProps[];
|
||||
|
@ -75,6 +76,7 @@ export type UserActionListProps = Omit<
|
|||
export const UserActionsList = React.memo(
|
||||
({
|
||||
caseUserActions,
|
||||
attachments,
|
||||
caseConnectors,
|
||||
userProfiles,
|
||||
currentUserProfile,
|
||||
|
@ -113,15 +115,15 @@ export const UserActionsList = React.memo(
|
|||
return [];
|
||||
}
|
||||
|
||||
return caseUserActions.reduce<EuiCommentProps[]>((comments, userAction, index) => {
|
||||
return caseUserActions.reduce<EuiCommentProps[]>((userActions, userAction, index) => {
|
||||
if (!isUserActionTypeSupported(userAction.type)) {
|
||||
return comments;
|
||||
return userActions;
|
||||
}
|
||||
|
||||
const builder = builderMap[userAction.type];
|
||||
|
||||
if (builder == null) {
|
||||
return comments;
|
||||
return userActions;
|
||||
}
|
||||
|
||||
const userActionBuilder = builder({
|
||||
|
@ -134,7 +136,7 @@ export const UserActionsList = React.memo(
|
|||
userAction,
|
||||
userProfiles,
|
||||
currentUserProfile,
|
||||
comments: caseData?.comments,
|
||||
attachments,
|
||||
index,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
|
@ -153,7 +155,7 @@ export const UserActionsList = React.memo(
|
|||
getRuleDetailsHref,
|
||||
onRuleDetailsClick,
|
||||
});
|
||||
return [...comments, ...userActionBuilder.build()];
|
||||
return [...userActions, ...userActionBuilder.build()];
|
||||
}, []);
|
||||
}, [
|
||||
caseUserActions,
|
||||
|
@ -165,6 +167,7 @@ export const UserActionsList = React.memo(
|
|||
persistableStateAttachmentTypeRegistry,
|
||||
userProfiles,
|
||||
currentUserProfile,
|
||||
attachments,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
|
|
@ -51,12 +51,6 @@ import type { UserProfile } from '@kbn/security-plugin/common';
|
|||
import { userProfiles } from '../user_profiles/api.mock';
|
||||
import { getCaseConnectorsMockResponse } from '../../common/mock/connectors';
|
||||
|
||||
export const getCase = async (
|
||||
caseId: string,
|
||||
includeComments: boolean = true,
|
||||
signal: AbortSignal
|
||||
): Promise<CaseUI> => Promise.resolve(basicCase);
|
||||
|
||||
export const resolveCase = async (
|
||||
caseId: string,
|
||||
includeComments: boolean = true,
|
||||
|
|
|
@ -22,7 +22,6 @@ import {
|
|||
deleteCases,
|
||||
deleteComment,
|
||||
getActionLicense,
|
||||
getCase,
|
||||
getCases,
|
||||
findCaseUserActions,
|
||||
getTags,
|
||||
|
@ -135,34 +134,6 @@ describe('Cases API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getCase', () => {
|
||||
beforeEach(() => {
|
||||
fetchMock.mockClear();
|
||||
fetchMock.mockResolvedValue(basicCaseSnake);
|
||||
});
|
||||
const data = basicCase.id;
|
||||
|
||||
it('should be called with correct check url, method, signal', async () => {
|
||||
await getCase(data, true, abortCtrl.signal);
|
||||
expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}`, {
|
||||
method: 'GET',
|
||||
query: { includeComments: true },
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return correct response', async () => {
|
||||
const resp = await getCase(data, true, abortCtrl.signal);
|
||||
expect(resp).toEqual(basicCase);
|
||||
});
|
||||
|
||||
it('should not covert to camel case registered attachments', async () => {
|
||||
fetchMock.mockResolvedValue(caseWithRegisteredAttachmentsSnake);
|
||||
const resp = await getCase(data, true, abortCtrl.signal);
|
||||
expect(resp).toEqual(caseWithRegisteredAttachments);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveCase', () => {
|
||||
const aliasTargetId = '12345';
|
||||
const basicResolveCase = {
|
||||
|
@ -180,7 +151,9 @@ describe('Cases API', () => {
|
|||
await resolveCase({ caseId, signal: abortCtrl.signal });
|
||||
expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${caseId}/resolve`, {
|
||||
method: 'GET',
|
||||
query: { includeComments: true },
|
||||
query: {
|
||||
includeComments: false,
|
||||
},
|
||||
signal: abortCtrl.signal,
|
||||
});
|
||||
});
|
||||
|
@ -540,6 +513,7 @@ describe('Cases API', () => {
|
|||
perPage: 10,
|
||||
total: 30,
|
||||
userActions: [...caseUserActionsWithRegisteredAttachmentsSnake],
|
||||
latestAttachments: [],
|
||||
};
|
||||
const filterActionType: CaseUserActionTypeWithAll = 'all';
|
||||
const sortOrder: 'asc' | 'desc' = 'asc';
|
||||
|
@ -557,16 +531,19 @@ describe('Cases API', () => {
|
|||
|
||||
it('should be called with correct check url, method, signal', async () => {
|
||||
await findCaseUserActions(basicCase.id, params, abortCtrl.signal);
|
||||
expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/user_actions/_find`, {
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: [],
|
||||
sortOrder: 'asc',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
},
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
`${CASES_INTERNAL_URL}/${basicCase.id}/user_actions/_find`,
|
||||
{
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: [],
|
||||
sortOrder: 'asc',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be called with action type user action and desc sort order', async () => {
|
||||
|
@ -575,30 +552,36 @@ describe('Cases API', () => {
|
|||
{ type: 'action', sortOrder: 'desc', page: 2, perPage: 15 },
|
||||
abortCtrl.signal
|
||||
);
|
||||
expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/user_actions/_find`, {
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: ['action'],
|
||||
sortOrder: 'desc',
|
||||
page: 2,
|
||||
perPage: 15,
|
||||
},
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
`${CASES_INTERNAL_URL}/${basicCase.id}/user_actions/_find`,
|
||||
{
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: ['action'],
|
||||
sortOrder: 'desc',
|
||||
page: 2,
|
||||
perPage: 15,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be called with user type user action and desc sort order', async () => {
|
||||
await findCaseUserActions(basicCase.id, { ...params, type: 'user' }, abortCtrl.signal);
|
||||
expect(fetchMock).toHaveBeenCalledWith(`${CASES_URL}/${basicCase.id}/user_actions/_find`, {
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: ['user'],
|
||||
sortOrder: 'asc',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
},
|
||||
});
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
`${CASES_INTERNAL_URL}/${basicCase.id}/user_actions/_find`,
|
||||
{
|
||||
method: 'GET',
|
||||
signal: abortCtrl.signal,
|
||||
query: {
|
||||
types: ['user'],
|
||||
sortOrder: 'asc',
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct response', async () => {
|
||||
|
|
|
@ -19,19 +19,18 @@ import type {
|
|||
CasesFindResponse,
|
||||
CaseUserActionStatsResponse,
|
||||
GetCaseConnectorsResponse,
|
||||
UserActionFindResponse,
|
||||
SingleCaseMetricsResponse,
|
||||
CustomFieldPutRequest,
|
||||
CasesSimilarResponse,
|
||||
AddObservableRequest,
|
||||
UpdateObservableRequest,
|
||||
UserActionInternalFindResponse,
|
||||
} from '../../common/types/api';
|
||||
import type {
|
||||
CaseConnectors,
|
||||
CaseUpdateRequest,
|
||||
FetchCasesProps,
|
||||
ResolvedCase,
|
||||
FindCaseUserActions,
|
||||
CaseUserActionTypeWithAll,
|
||||
CaseUserActionsStats,
|
||||
CaseUsers,
|
||||
|
@ -41,6 +40,7 @@ import type {
|
|||
CaseUICustomField,
|
||||
SimilarCasesProps,
|
||||
CasesSimilarResponseUI,
|
||||
InternalFindCaseUserActions,
|
||||
} from '../../common/ui/types';
|
||||
import { SortFieldCase } from '../../common/ui/types';
|
||||
import {
|
||||
|
@ -81,6 +81,7 @@ import {
|
|||
convertCasesToCamelCase,
|
||||
convertCaseResolveToCamelCase,
|
||||
convertSimilarCasesToCamel,
|
||||
convertAttachmentsToCamelCase,
|
||||
} from '../api/utils';
|
||||
|
||||
import type {
|
||||
|
@ -105,37 +106,18 @@ import {
|
|||
} from './utils';
|
||||
import { decodeCasesFindResponse, decodeCasesSimilarResponse } from '../api/decoders';
|
||||
|
||||
export const getCase = async (
|
||||
caseId: string,
|
||||
includeComments: boolean = true,
|
||||
signal: AbortSignal
|
||||
): Promise<CaseUI> => {
|
||||
const response = await KibanaServices.get().http.fetch<Case>(getCaseDetailsUrl(caseId), {
|
||||
method: 'GET',
|
||||
query: {
|
||||
includeComments,
|
||||
},
|
||||
signal,
|
||||
});
|
||||
return convertCaseToCamelCase(decodeCaseResponse(response));
|
||||
};
|
||||
|
||||
export const resolveCase = async ({
|
||||
caseId,
|
||||
includeComments = true,
|
||||
signal,
|
||||
}: {
|
||||
caseId: string;
|
||||
includeComments?: boolean;
|
||||
signal?: AbortSignal;
|
||||
}): Promise<ResolvedCase> => {
|
||||
const response = await KibanaServices.get().http.fetch<CaseResolveResponse>(
|
||||
`${getCaseDetailsUrl(caseId)}/resolve`,
|
||||
{
|
||||
method: 'GET',
|
||||
query: {
|
||||
includeComments,
|
||||
},
|
||||
query: { includeComments: false },
|
||||
signal,
|
||||
}
|
||||
);
|
||||
|
@ -211,7 +193,7 @@ export const findCaseUserActions = async (
|
|||
perPage: number;
|
||||
},
|
||||
signal?: AbortSignal
|
||||
): Promise<FindCaseUserActions> => {
|
||||
): Promise<InternalFindCaseUserActions> => {
|
||||
const query = {
|
||||
types: params.type !== 'all' ? [params.type] : [],
|
||||
sortOrder: params.sortOrder,
|
||||
|
@ -219,7 +201,7 @@ export const findCaseUserActions = async (
|
|||
perPage: params.perPage,
|
||||
};
|
||||
|
||||
const response = await KibanaServices.get().http.fetch<UserActionFindResponse>(
|
||||
const response = await KibanaServices.get().http.fetch<UserActionInternalFindResponse>(
|
||||
getCaseFindUserActionsUrl(caseId),
|
||||
{
|
||||
method: 'GET',
|
||||
|
@ -233,6 +215,7 @@ export const findCaseUserActions = async (
|
|||
userActions: convertUserActionsToCamelCase(
|
||||
decodeCaseUserActionsResponse(response.userActions)
|
||||
) as UserActionUI[],
|
||||
latestAttachments: convertAttachmentsToCamelCase(response.latestAttachments),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@ import type {
|
|||
CasesMetrics,
|
||||
ExternalReferenceAttachmentUI,
|
||||
PersistableStateAttachmentUI,
|
||||
FindCaseUserActions,
|
||||
CaseUsers,
|
||||
CaseUserActionsStats,
|
||||
CasesFindResponseUI,
|
||||
|
@ -49,6 +48,7 @@ import type {
|
|||
CasesConfigurationUITemplate,
|
||||
CasesSimilarResponseUI,
|
||||
ObservableUI,
|
||||
InternalFindCaseUserActions,
|
||||
} from '../../common/ui/types';
|
||||
import { CaseMetricsFeature } from '../../common/types/api';
|
||||
import { OBSERVABLE_TYPE_IPV4, SECURITY_SOLUTION_OWNER } from '../../common/constants';
|
||||
|
@ -974,11 +974,12 @@ export const caseUserActionsWithRegisteredAttachments: UserActionUI[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export const findCaseUserActionsResponse: FindCaseUserActions = {
|
||||
export const findCaseUserActionsResponse: InternalFindCaseUserActions = {
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 30,
|
||||
userActions: [...caseUserActionsWithRegisteredAttachments],
|
||||
latestAttachments: [],
|
||||
};
|
||||
|
||||
export const getCaseUserActionsStatsResponse: CaseUserActionsStats = {
|
||||
|
|
|
@ -52,6 +52,7 @@ describe('UseFindCaseUserActions', () => {
|
|||
expect.objectContaining({
|
||||
...initialData,
|
||||
data: {
|
||||
latestAttachments: [],
|
||||
userActions: [...findCaseUserActionsResponse.userActions],
|
||||
total: 30,
|
||||
perPage: 10,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { FindCaseUserActions, CaseUserActionTypeWithAll } from '../../common/ui/types';
|
||||
import type { CaseUserActionTypeWithAll, InternalFindCaseUserActions } from '../../common/ui/types';
|
||||
import { findCaseUserActions } from './api';
|
||||
import type { ServerError } from '../types';
|
||||
import { useCasesToast } from '../common/use_cases_toast';
|
||||
|
@ -25,7 +25,7 @@ export const useFindCaseUserActions = (
|
|||
) => {
|
||||
const { showErrorToast } = useCasesToast();
|
||||
|
||||
return useQuery<FindCaseUserActions, ServerError>(
|
||||
return useQuery<InternalFindCaseUserActions, ServerError>(
|
||||
casesQueriesKeys.caseUserActions(caseId, params),
|
||||
async ({ signal }) => findCaseUserActions(caseId, params, signal),
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ export const useGetCase = (caseId: string) => {
|
|||
const toasts = useToasts();
|
||||
return useQuery<ResolvedCase, ServerError>(
|
||||
casesQueriesKeys.case(caseId),
|
||||
({ signal }) => resolveCase({ caseId, includeComments: true, signal }),
|
||||
({ signal }) => resolveCase({ caseId, signal }),
|
||||
{
|
||||
onError: (error: ServerError) => {
|
||||
if (error.name !== 'AbortError') {
|
||||
|
|
|
@ -55,6 +55,7 @@ describe('UseInfiniteFindCaseUserActions', () => {
|
|||
data: {
|
||||
pages: [
|
||||
{
|
||||
latestAttachments: [],
|
||||
userActions: [...findCaseUserActionsResponse.userActions],
|
||||
total: 30,
|
||||
perPage: 10,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import type { FindCaseUserActions, CaseUserActionTypeWithAll } from '../../common/ui/types';
|
||||
import type { InternalFindCaseUserActions, CaseUserActionTypeWithAll } from '../../common/ui/types';
|
||||
import { findCaseUserActions } from './api';
|
||||
import type { ServerError } from '../types';
|
||||
import { useCasesToast } from '../common/use_cases_toast';
|
||||
|
@ -25,7 +25,7 @@ export const useInfiniteFindCaseUserActions = (
|
|||
const { showErrorToast } = useCasesToast();
|
||||
const abortCtrlRef = new AbortController();
|
||||
|
||||
return useInfiniteQuery<FindCaseUserActions, ServerError>(
|
||||
return useInfiniteQuery<InternalFindCaseUserActions, ServerError>(
|
||||
casesQueriesKeys.caseUserActions(caseId, params),
|
||||
async ({ pageParam = 1 }) => {
|
||||
return findCaseUserActions(caseId, { ...params, page: pageParam }, abortCtrlRef.signal);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { postObservableRoute } from './observables/post_observable';
|
|||
import { similarCaseRoute } from './cases/similar';
|
||||
import { patchObservableRoute } from './observables/patch_observable';
|
||||
import { deleteObservableRoute } from './observables/delete_observable';
|
||||
import { findUserActionsRoute } from './internal/find_user_actions';
|
||||
|
||||
export const getInternalRoutes = (userProfileService: UserProfileService) =>
|
||||
[
|
||||
|
@ -44,4 +45,5 @@ export const getInternalRoutes = (userProfileService: UserProfileService) =>
|
|||
patchObservableRoute,
|
||||
deleteObservableRoute,
|
||||
similarCaseRoute,
|
||||
findUserActionsRoute,
|
||||
] as CaseRoute[];
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* 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 { findUserActionsRoute } from './find_user_actions';
|
||||
|
||||
const userActionsMockData = {
|
||||
userActions: [
|
||||
{
|
||||
type: 'create_case',
|
||||
payload: {
|
||||
connector: { id: 'none', type: '.none', fields: null, name: 'none' },
|
||||
title: 'My Case',
|
||||
tags: [],
|
||||
description: 'my case desc.',
|
||||
settings: { syncAlerts: false },
|
||||
owner: 'cases',
|
||||
severity: 'low',
|
||||
assignees: [],
|
||||
status: 'open',
|
||||
category: null,
|
||||
customFields: [],
|
||||
},
|
||||
created_at: '2025-01-07T13:31:55.427Z',
|
||||
created_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
owner: 'cases',
|
||||
action: 'create',
|
||||
comment_id: null,
|
||||
id: 'e11e39f5-ea29-4cbc-981b-1508cafdb0ad',
|
||||
version: 'WzIsMV0=',
|
||||
},
|
||||
{
|
||||
payload: { comment: { comment: 'First comment', type: 'user', owner: 'cases' } },
|
||||
type: 'comment',
|
||||
created_at: '2025-01-07T13:32:01.314Z',
|
||||
created_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
owner: 'cases',
|
||||
action: 'create',
|
||||
comment_id: '601a03cf-71a0-4949-9407-97cf372b313b',
|
||||
id: '71f67236-f2f5-4cfe-964d-a4103a9717f2',
|
||||
version: 'WzUsMV0=',
|
||||
},
|
||||
{
|
||||
payload: { comment: { comment: 'Second comment', type: 'user', owner: 'cases' } },
|
||||
type: 'comment',
|
||||
created_at: '2025-01-07T13:32:08.045Z',
|
||||
created_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
owner: 'cases',
|
||||
action: 'create',
|
||||
comment_id: '2cd1eb7d-ff8a-4c0e-b904-0beb64ab166a',
|
||||
id: '00414cd9-b51a-4b85-a7d3-cb39de4d61db',
|
||||
version: 'WzgsMV0=',
|
||||
},
|
||||
{
|
||||
payload: { comment: { comment: 'Edited first comment', type: 'user', owner: 'cases' } },
|
||||
type: 'comment',
|
||||
created_at: '2025-01-07T13:32:18.160Z',
|
||||
created_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
owner: 'cases',
|
||||
action: 'update',
|
||||
comment_id: '123e4567-e89b-12d3-a456-426614174000',
|
||||
id: '675cc9a3-5445-4aaa-ad65-21241f095546',
|
||||
version: 'WzExLDFd',
|
||||
},
|
||||
],
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 4,
|
||||
};
|
||||
|
||||
const attachmentsMockData = {
|
||||
attachments: [
|
||||
{
|
||||
comment: 'Edited first comment',
|
||||
type: 'user',
|
||||
owner: 'cases',
|
||||
created_at: '2025-01-07T13:32:01.283Z',
|
||||
created_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
updated_at: '2025-01-07T13:32:18.127Z',
|
||||
updated_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
id: '601a03cf-71a0-4949-9407-97cf372b313b',
|
||||
version: 'WzksMV0=',
|
||||
},
|
||||
{
|
||||
comment: 'Second comment',
|
||||
type: 'user',
|
||||
owner: 'cases',
|
||||
created_at: '2025-01-07T13:32:08.015Z',
|
||||
created_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
id: '2cd1eb7d-ff8a-4c0e-b904-0beb64ab166a',
|
||||
version: 'WzYsMV0=',
|
||||
},
|
||||
{
|
||||
comment: 'Edited first comment',
|
||||
type: 'user',
|
||||
owner: 'cases',
|
||||
created_at: '2025-01-07T13:32:01.283Z',
|
||||
created_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
username: 'elastic',
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
updated_at: '2025-01-07T13:32:18.127Z',
|
||||
updated_by: {
|
||||
username: 'elastic',
|
||||
full_name: null,
|
||||
email: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
},
|
||||
id: '123e4567-e89b-12d3-a456-426614174000',
|
||||
version: 'WzksMV0=',
|
||||
},
|
||||
],
|
||||
errors: [],
|
||||
};
|
||||
|
||||
describe('findUserActionsRoute', () => {
|
||||
const response = { ok: jest.fn() };
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return user actions and latest attachments', async () => {
|
||||
const casesClientMock = {
|
||||
userActions: {
|
||||
find: jest.fn().mockResolvedValue(userActionsMockData),
|
||||
},
|
||||
attachments: {
|
||||
bulkGet: jest.fn().mockResolvedValue(attachmentsMockData),
|
||||
},
|
||||
};
|
||||
const context = { cases: { getCasesClient: jest.fn().mockResolvedValue(casesClientMock) } };
|
||||
const request = {
|
||||
params: {
|
||||
case_id: 'my_fake_case_id',
|
||||
},
|
||||
query: '',
|
||||
};
|
||||
|
||||
// @ts-expect-error: mocking necessary properties for handler logic only, no Kibana platform
|
||||
await findUserActionsRoute.handler({ context, request, response });
|
||||
|
||||
expect(casesClientMock.attachments.bulkGet).toHaveBeenCalledWith({
|
||||
attachmentIDs: [
|
||||
userActionsMockData.userActions[1].comment_id,
|
||||
userActionsMockData.userActions[2].comment_id,
|
||||
userActionsMockData.userActions[3].comment_id,
|
||||
],
|
||||
caseID: 'my_fake_case_id',
|
||||
});
|
||||
expect(response.ok).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
latestAttachments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
comment: 'Edited first comment',
|
||||
created_at: '2025-01-07T13:32:01.283Z',
|
||||
created_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
username: 'elastic',
|
||||
},
|
||||
id: '601a03cf-71a0-4949-9407-97cf372b313b',
|
||||
owner: 'cases',
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
type: 'user',
|
||||
updated_at: '2025-01-07T13:32:18.127Z',
|
||||
updated_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
username: 'elastic',
|
||||
},
|
||||
version: 'WzksMV0=',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return empty attachments when no commentId', async () => {
|
||||
const casesClientMock = {
|
||||
userActions: {
|
||||
// userActionsMockData.userActions[0] must have no commentId
|
||||
find: jest.fn().mockResolvedValue({ userActions: [userActionsMockData.userActions[0]] }),
|
||||
},
|
||||
attachments: {
|
||||
bulkGet: jest.fn().mockResolvedValue(attachmentsMockData),
|
||||
},
|
||||
};
|
||||
const context = { cases: { getCasesClient: jest.fn().mockResolvedValue(casesClientMock) } };
|
||||
const request = {
|
||||
params: {
|
||||
case_id: 'my_fake_case_id',
|
||||
},
|
||||
query: '',
|
||||
};
|
||||
|
||||
// @ts-expect-error: Kibana platform types are mocked for testing, only implementing necessary properties for handler logic
|
||||
await findUserActionsRoute.handler({ context, request, response });
|
||||
|
||||
expect(response.ok).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
latestAttachments: [],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should filter repeated comment_ids', async () => {
|
||||
userActionsMockData.userActions[1].comment_id = userActionsMockData.userActions[2].comment_id;
|
||||
const casesClientMock = {
|
||||
userActions: {
|
||||
find: jest.fn().mockResolvedValue(userActionsMockData),
|
||||
},
|
||||
attachments: {
|
||||
bulkGet: jest.fn().mockResolvedValue(attachmentsMockData),
|
||||
},
|
||||
};
|
||||
const context = { cases: { getCasesClient: jest.fn().mockResolvedValue(casesClientMock) } };
|
||||
const request = {
|
||||
params: {
|
||||
case_id: 'my_fake_case_id',
|
||||
},
|
||||
query: '',
|
||||
};
|
||||
|
||||
// @ts-expect-error: mocking necessary properties for handler logic only, no Kibana platform
|
||||
await findUserActionsRoute.handler({ context, request, response });
|
||||
|
||||
expect(casesClientMock.attachments.bulkGet).toHaveBeenCalledWith({
|
||||
attachmentIDs: [
|
||||
userActionsMockData.userActions[1].comment_id,
|
||||
userActionsMockData.userActions[3].comment_id,
|
||||
],
|
||||
caseID: 'my_fake_case_id',
|
||||
});
|
||||
expect(response.ok).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
latestAttachments: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
comment: 'Edited first comment',
|
||||
created_at: '2025-01-07T13:32:01.283Z',
|
||||
created_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
username: 'elastic',
|
||||
},
|
||||
id: '601a03cf-71a0-4949-9407-97cf372b313b',
|
||||
owner: 'cases',
|
||||
pushed_at: null,
|
||||
pushed_by: null,
|
||||
type: 'user',
|
||||
updated_at: '2025-01-07T13:32:18.127Z',
|
||||
updated_by: {
|
||||
email: null,
|
||||
full_name: null,
|
||||
profile_uid: 'u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0',
|
||||
username: 'elastic',
|
||||
},
|
||||
version: 'WzksMV0=',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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 { schema } from '@kbn/config-schema';
|
||||
|
||||
import { isCommentUserAction } from '../../../../common/utils/user_actions';
|
||||
import type { attachmentApiV1, userActionApiV1 } from '../../../../common/types/api';
|
||||
import { INTERNAL_CASE_FIND_USER_ACTIONS_URL } from '../../../../common/constants';
|
||||
import { createCaseError } from '../../../common/error';
|
||||
import { createCasesRoute } from '../create_cases_route';
|
||||
|
||||
const params = {
|
||||
params: schema.object({
|
||||
case_id: schema.string(),
|
||||
}),
|
||||
};
|
||||
|
||||
export const findUserActionsRoute = createCasesRoute({
|
||||
method: 'get',
|
||||
path: INTERNAL_CASE_FIND_USER_ACTIONS_URL,
|
||||
params,
|
||||
routerOptions: {
|
||||
access: 'public',
|
||||
summary: 'Get user actions by case',
|
||||
tags: ['oas-tag:cases'],
|
||||
},
|
||||
handler: async ({ context, request, response }) => {
|
||||
try {
|
||||
const caseContext = await context.cases;
|
||||
const casesClient = await caseContext.getCasesClient();
|
||||
const caseId = request.params.case_id;
|
||||
const options = request.query as userActionApiV1.UserActionFindRequest;
|
||||
|
||||
const userActionsResponse: userActionApiV1.UserActionFindResponse =
|
||||
await casesClient.userActions.find({
|
||||
caseId,
|
||||
params: options,
|
||||
});
|
||||
|
||||
const uniqueCommentIds: Set<string> = new Set();
|
||||
for (const action of userActionsResponse.userActions) {
|
||||
if (isCommentUserAction(action) && action.comment_id) {
|
||||
uniqueCommentIds.add(action.comment_id);
|
||||
}
|
||||
}
|
||||
const commentIds = Array.from(uniqueCommentIds);
|
||||
|
||||
let attachmentRes: attachmentApiV1.BulkGetAttachmentsResponse = {
|
||||
attachments: [],
|
||||
errors: [],
|
||||
};
|
||||
|
||||
if (commentIds.length > 0) {
|
||||
attachmentRes = await casesClient.attachments.bulkGet({
|
||||
caseID: caseId,
|
||||
attachmentIDs: commentIds,
|
||||
});
|
||||
}
|
||||
|
||||
const res: userActionApiV1.UserActionInternalFindResponse = {
|
||||
...userActionsResponse,
|
||||
latestAttachments: attachmentRes.attachments,
|
||||
};
|
||||
|
||||
return response.ok({
|
||||
body: res,
|
||||
});
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to retrieve case details in route case id: ${request.params.case_id}: \n${error}`,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
|
@ -53,11 +53,14 @@ import {
|
|||
GetRelatedCasesByAlertResponse,
|
||||
SimilarCasesSearchRequest,
|
||||
CasesSimilarResponse,
|
||||
UserActionFindRequest,
|
||||
UserActionInternalFindResponse,
|
||||
} from '@kbn/cases-plugin/common/types/api';
|
||||
import {
|
||||
getCaseCreateObservableUrl,
|
||||
getCaseUpdateObservableUrl,
|
||||
getCaseDeleteObservableUrl,
|
||||
getCaseFindUserActionsUrl,
|
||||
} from '@kbn/cases-plugin/common/api';
|
||||
import { User } from '../authentication/types';
|
||||
import { superUser } from '../authentication/users';
|
||||
|
@ -975,3 +978,25 @@ export const similarCases = async ({
|
|||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const findInternalCaseUserActions = async ({
|
||||
supertest,
|
||||
caseID,
|
||||
options = {},
|
||||
expectedHttpCode = 200,
|
||||
auth = { user: superUser, space: null },
|
||||
}: {
|
||||
supertest: SuperTest.Agent;
|
||||
caseID: string;
|
||||
options?: UserActionFindRequest;
|
||||
expectedHttpCode?: number;
|
||||
auth?: { user: User; space: string | null };
|
||||
}): Promise<UserActionInternalFindResponse> => {
|
||||
const { body: userActions } = await supertest
|
||||
.get(`${getSpaceUrlPrefix(auth.space)}${getCaseFindUserActionsUrl(caseID)}`)
|
||||
.query(options)
|
||||
.auth(auth.user.username, auth.user.password)
|
||||
.expect(expectedHttpCode);
|
||||
|
||||
return userActions;
|
||||
};
|
||||
|
|
|
@ -58,6 +58,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
|
|||
loadTestFile(require.resolve('./internal/bulk_delete_file_attachments'));
|
||||
loadTestFile(require.resolve('./internal/search_cases'));
|
||||
loadTestFile(require.resolve('./internal/replace_custom_field'));
|
||||
loadTestFile(require.resolve('./internal/find_user_actions.ts'));
|
||||
|
||||
/**
|
||||
* Attachments framework
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue