Skip to content

Commit

Permalink
[SIEM] [CASES] bug/clean up phase I (#61354)
Browse files Browse the repository at this point in the history
* clean up comments + add update_by/update_at  case when comment are added/updated + return all comments

* add refresh button + get a better interaction between user + fix bug with pushed data + fix three dot on detail page

* fix i18m

* review I

* review II
  • Loading branch information
XavierM committed Mar 27, 2020
1 parent 22e550c commit 898c6b9
Show file tree
Hide file tree
Showing 38 changed files with 746 additions and 562 deletions.
22 changes: 10 additions & 12 deletions x-pack/legacy/plugins/siem/public/containers/case/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import {
CaseResponse,
CasesResponse,
CasesFindResponse,
CaseRequest,
CasePatchRequest,
CasePostRequest,
CasesStatusResponse,
CommentRequest,
CommentResponse,
User,
CaseUserActionsResponse,
CaseExternalServiceRequest,
Expand All @@ -26,7 +26,6 @@ import {
BulkUpdateStatus,
Case,
CasesStatus,
Comment,
FetchCasesProps,
SortFieldCase,
CaseUserActions,
Expand All @@ -40,7 +39,6 @@ import {
decodeCasesResponse,
decodeCasesFindResponse,
decodeCasesStatusResponse,
decodeCommentResponse,
decodeCaseUserActionsResponse,
decodeServiceConnectorCaseResponse,
} from './utils';
Expand Down Expand Up @@ -123,7 +121,7 @@ export const getCases = async ({
return convertAllCasesToCamel(decodeCasesFindResponse(response));
};

export const postCase = async (newCase: CaseRequest): Promise<Case> => {
export const postCase = async (newCase: CasePostRequest): Promise<Case> => {
const response = await KibanaServices.get().http.fetch<CaseResponse>(CASES_URL, {
method: 'POST',
body: JSON.stringify(newCase),
Expand All @@ -133,7 +131,7 @@ export const postCase = async (newCase: CaseRequest): Promise<Case> => {

export const patchCase = async (
caseId: string,
updatedCase: Partial<CaseRequest>,
updatedCase: Pick<CasePatchRequest, 'description' | 'status' | 'tags' | 'title'>,
version: string
): Promise<Case[]> => {
const response = await KibanaServices.get().http.fetch<CasesResponse>(CASES_URL, {
Expand All @@ -151,31 +149,31 @@ export const patchCasesStatus = async (cases: BulkUpdateStatus[]): Promise<Case[
return convertToCamelCase<CasesResponse, Case[]>(decodeCasesResponse(response));
};

export const postComment = async (newComment: CommentRequest, caseId: string): Promise<Comment> => {
const response = await KibanaServices.get().http.fetch<CommentResponse>(
export const postComment = async (newComment: CommentRequest, caseId: string): Promise<Case> => {
const response = await KibanaServices.get().http.fetch<CaseResponse>(
`${CASES_URL}/${caseId}/comments`,
{
method: 'POST',
body: JSON.stringify(newComment),
}
);
return convertToCamelCase<CommentResponse, Comment>(decodeCommentResponse(response));
return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response));
};

export const patchComment = async (
caseId: string,
commentId: string,
commentUpdate: string,
version: string
): Promise<Partial<Comment>> => {
const response = await KibanaServices.get().http.fetch<CommentResponse>(
): Promise<Case> => {
const response = await KibanaServices.get().http.fetch<CaseResponse>(
`${CASES_URL}/${caseId}/comments`,
{
method: 'PATCH',
body: JSON.stringify({ comment: commentUpdate, id: commentId, version }),
}
);
return convertToCamelCase<CommentResponse, Comment>(decodeCommentResponse(response));
return convertToCamelCase<CaseResponse, Case>(decodeCaseResponse(response));
};

export const deleteCases = async (caseIds: string[]): Promise<boolean> => {
Expand Down
27 changes: 21 additions & 6 deletions x-pack/legacy/plugins/siem/public/containers/case/use_get_case.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { useEffect, useReducer } from 'react';
import { useEffect, useReducer, useCallback } from 'react';

import { Case } from './types';
import * as i18n from './translations';
Expand All @@ -20,7 +20,8 @@ interface CaseState {
type Action =
| { type: 'FETCH_INIT' }
| { type: 'FETCH_SUCCESS'; payload: Case }
| { type: 'FETCH_FAILURE' };
| { type: 'FETCH_FAILURE' }
| { type: 'UPDATE_CASE'; payload: Case };

const dataFetchReducer = (state: CaseState, action: Action): CaseState => {
switch (action.type) {
Expand All @@ -43,6 +44,11 @@ const dataFetchReducer = (state: CaseState, action: Action): CaseState => {
isLoading: false,
isError: true,
};
case 'UPDATE_CASE':
return {
...state,
data: action.payload,
};
default:
return state;
}
Expand All @@ -67,15 +73,24 @@ const initialData: Case = {
version: '',
};

export const useGetCase = (caseId: string): CaseState => {
interface UseGetCase extends CaseState {
fetchCase: () => void;
updateCase: (newCase: Case) => void;
}

export const useGetCase = (caseId: string): UseGetCase => {
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: true,
isError: false,
data: initialData,
});
const [, dispatchToaster] = useStateToaster();

const callFetch = () => {
const updateCase = useCallback((newCase: Case) => {
dispatch({ type: 'UPDATE_CASE', payload: newCase });
}, []);

const callFetch = useCallback(async () => {
let didCancel = false;
const fetchData = async () => {
dispatch({ type: 'FETCH_INIT' });
Expand All @@ -99,10 +114,10 @@ export const useGetCase = (caseId: string): CaseState => {
return () => {
didCancel = true;
};
};
}, [caseId]);

useEffect(() => {
callFetch();
}, [caseId]);
return state;
return { ...state, fetchCase: callFetch, updateCase };
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { useReducer, useCallback } from 'react';

import { CaseRequest } from '../../../../../../plugins/case/common/api';
import { CasePostRequest } from '../../../../../../plugins/case/common/api';
import { errorToToaster, useStateToaster } from '../../components/toasters';
import { postCase } from './api';
import * as i18n from './translations';
Expand Down Expand Up @@ -49,7 +49,7 @@ const dataFetchReducer = (state: NewCaseState, action: Action): NewCaseState =>
};

interface UsePostCase extends NewCaseState {
postCase: (data: CaseRequest) => void;
postCase: (data: CasePostRequest) => void;
}
export const usePostCase = (): UsePostCase => {
const [state, dispatch] = useReducer(dataFetchReducer, {
Expand All @@ -59,11 +59,11 @@ export const usePostCase = (): UsePostCase => {
});
const [, dispatchToaster] = useStateToaster();

const postMyCase = useCallback(async (data: CaseRequest) => {
const postMyCase = useCallback(async (data: CasePostRequest) => {
let cancel = false;
try {
dispatch({ type: 'FETCH_INIT' });
const response = await postCase({ ...data, status: 'open' });
const response = await postCase(data);
if (!cancel) {
dispatch({
type: 'FETCH_SUCCESS',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,28 @@ import { errorToToaster, useStateToaster } from '../../components/toasters';

import { postComment } from './api';
import * as i18n from './translations';
import { Comment } from './types';
import { Case } from './types';

interface NewCommentState {
commentData: Comment | null;
isLoading: boolean;
isError: boolean;
caseId: string;
}
type Action =
| { type: 'RESET_COMMENT_DATA' }
| { type: 'FETCH_INIT' }
| { type: 'FETCH_SUCCESS'; payload: Comment }
| { type: 'FETCH_FAILURE' };
type Action = { type: 'FETCH_INIT' } | { type: 'FETCH_SUCCESS' } | { type: 'FETCH_FAILURE' };

const dataFetchReducer = (state: NewCommentState, action: Action): NewCommentState => {
switch (action.type) {
case 'RESET_COMMENT_DATA':
return {
...state,
commentData: null,
};
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false,
};
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isError: false,
commentData: action.payload ?? null,
};
case 'FETCH_FAILURE':
return {
...state,
isLoading: false,
isError: true,
};
Expand All @@ -57,43 +42,42 @@ const dataFetchReducer = (state: NewCommentState, action: Action): NewCommentSta
};

interface UsePostComment extends NewCommentState {
postComment: (data: CommentRequest) => void;
resetCommentData: () => void;
postComment: (data: CommentRequest, updateCase: (newCase: Case) => void) => void;
}

export const usePostComment = (caseId: string): UsePostComment => {
const [state, dispatch] = useReducer(dataFetchReducer, {
commentData: null,
isLoading: false,
isError: false,
caseId,
});
const [, dispatchToaster] = useStateToaster();

const postMyComment = useCallback(async (data: CommentRequest) => {
let cancel = false;
try {
dispatch({ type: 'FETCH_INIT' });
const response = await postComment(data, state.caseId);
if (!cancel) {
dispatch({ type: 'FETCH_SUCCESS', payload: response });
}
} catch (error) {
if (!cancel) {
errorToToaster({
title: i18n.ERROR_TITLE,
error: error.body && error.body.message ? new Error(error.body.message) : error,
dispatchToaster,
});
dispatch({ type: 'FETCH_FAILURE' });
const postMyComment = useCallback(
async (data: CommentRequest, updateCase: (newCase: Case) => void) => {
let cancel = false;
try {
dispatch({ type: 'FETCH_INIT' });
const response = await postComment(data, caseId);
if (!cancel) {
dispatch({ type: 'FETCH_SUCCESS' });
updateCase(response);
}
} catch (error) {
if (!cancel) {
errorToToaster({
title: i18n.ERROR_TITLE,
error: error.body && error.body.message ? new Error(error.body.message) : error,
dispatchToaster,
});
dispatch({ type: 'FETCH_FAILURE' });
}
}
}
return () => {
cancel = true;
};
}, []);

const resetCommentData = useCallback(() => dispatch({ type: 'RESET_COMMENT_DATA' }), []);
return () => {
cancel = true;
};
},
[caseId]
);

return { ...state, postComment: postMyComment, resetCommentData };
return { ...state, postComment: postMyComment };
};
Original file line number Diff line number Diff line change
Expand Up @@ -143,31 +143,42 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => {
updatedAt,
updatedBy,
} = myCase;

return {
caseId,
createdAt,
createdBy: {
fullName: createdBy.fullName ?? null,
username: createdBy?.username,
},
comments: comments.map(c => ({
commentId: c.id,
comment: c.comment,
createdAt: c.createdAt,
createdBy: {
fullName: c.createdBy.fullName ?? null,
username: c.createdBy.username,
},
updatedAt: c.updatedAt,
updatedBy:
c.updatedBy != null
? {
fullName: c.updatedBy.fullName ?? null,
username: c.updatedBy.username,
}
: null,
})),
comments: comments
.filter(c => {
const lastPush = c.pushedAt != null ? new Date(c.pushedAt) : null;
const lastUpdate = c.updatedAt != null ? new Date(c.updatedAt) : null;
if (
lastPush === null ||
(lastPush != null && lastUpdate != null && lastPush.getTime() < lastUpdate?.getTime())
) {
return true;
}
return false;
})
.map(c => ({
commentId: c.id,
comment: c.comment,
createdAt: c.createdAt,
createdBy: {
fullName: c.createdBy.fullName ?? null,
username: c.createdBy.username,
},
updatedAt: c.updatedAt,
updatedBy:
c.updatedBy != null
? {
fullName: c.updatedBy.fullName ?? null,
username: c.updatedBy.username,
}
: null,
})),
description,
incidentId: externalService?.externalId ?? null,
title,
Expand Down
Loading

0 comments on commit 898c6b9

Please sign in to comment.