Skip to content

Commit

Permalink
[ResponseOps][Cases] Preferring profile uid for recent cases (#140861)
Browse files Browse the repository at this point in the history
* Preferring profile uid for recent cases

* removing uncommented code

* Fixing ui crash

* Adding integration test
  • Loading branch information
jonathan-buttner committed Sep 20, 2022
1 parent c92f2b9 commit a71f965
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 177 deletions.
64 changes: 58 additions & 6 deletions x-pack/plugins/cases/public/components/recent_cases/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
*/

import React from 'react';
import { configure } from '@testing-library/react';
import { configure, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import RecentCases, { RecentCasesProps } from '.';
import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock';
import { useGetCasesMockState } from '../../containers/mock';
import { useCurrentUser } from '../../common/lib/kibana/hooks';
import { useGetCases } from '../../containers/use_get_cases';
import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile';
import { userProfiles } from '../../containers/user_profiles/api.mock';

jest.mock('../../containers/user_profiles/use_get_current_user_profile');
jest.mock('../../containers/use_get_cases');
jest.mock('../../common/lib/kibana/hooks');
jest.mock('../../common/navigation/hooks');
Expand All @@ -27,19 +30,25 @@ const mockData = {
...useGetCasesMockState,
};

const useGetCurrentUserProfileMock = useGetCurrentUserProfile as jest.Mock;
const useGetCasesMock = useGetCases as jest.Mock;
const useCurrentUserMock = useCurrentUser as jest.Mock;

describe('RecentCases', () => {
let appMockRender: AppMockRenderer;
beforeEach(() => {
jest.clearAllMocks();
useGetCurrentUserProfileMock.mockReturnValue({
data: userProfiles[0],
isLoading: false,
});
useGetCasesMock.mockImplementation(() => mockData);
useCurrentUserMock.mockResolvedValue({
useCurrentUserMock.mockReturnValue({
email: 'elastic@elastic.co',
fullName: 'Elastic',
username: 'elastic',
});

appMockRender = createAppMockRenderer();
});

Expand Down Expand Up @@ -78,7 +87,7 @@ describe('RecentCases', () => {
});
});

it('sets the reporter filters correctly', () => {
it('sets the reporter filters correctly', async () => {
const { getByTestId } = appMockRender.render(
<TestProviders>
<RecentCases {...defaultProps} />
Expand All @@ -91,12 +100,21 @@ describe('RecentCases', () => {
});

// apply the filter
const myRecentCasesElement = getByTestId('myRecentlyReported');
userEvent.click(myRecentCasesElement);
await waitFor(() => {
const myRecentCasesElement = getByTestId('myRecentlyReported');
userEvent.click(myRecentCasesElement);
});

expect(useGetCasesMock).toHaveBeenLastCalledWith({
filterOptions: {
reporters: [{ email: undefined, full_name: undefined, username: undefined }],
reporters: [
{
email: 'damaged_raccoon@elastic.co',
full_name: 'Damaged Raccoon',
profile_uid: 'u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0',
username: 'damaged_raccoon',
},
],
},
queryParams: { perPage: 10 },
});
Expand All @@ -112,4 +130,38 @@ describe('RecentCases', () => {
queryParams: { perPage: 10 },
});
});

it('sets the reporter filters to the user info without the profile uid when it cannot find the current user profile', async () => {
useGetCurrentUserProfileMock.mockReturnValue({ data: undefined, isLoading: false });

const { getByTestId } = appMockRender.render(
<TestProviders>
<RecentCases {...defaultProps} />
</TestProviders>
);

expect(useGetCasesMock).toHaveBeenCalledWith({
filterOptions: { reporters: [] },
queryParams: { perPage: 10 },
});

// apply the filter
await waitFor(() => {
const myRecentCasesElement = getByTestId('myRecentlyReported');
userEvent.click(myRecentCasesElement);
});

expect(useGetCasesMock).toHaveBeenLastCalledWith({
filterOptions: {
reporters: [
{
email: 'elastic@elastic.co',
full_name: 'Elastic',
username: 'elastic',
},
],
},
queryParams: { perPage: 10 },
});
});
});
131 changes: 91 additions & 40 deletions x-pack/plugins/cases/public/components/recent_cases/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,39 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiText, EuiTitle } from
import React, { useCallback, useMemo, useState } from 'react';

import { QueryClientProvider } from '@tanstack/react-query';
import { UserProfile } from '@kbn/user-profile-components';
import * as i18n from './translations';
import { LinkAnchor } from '../links';
import { RecentCasesFilters } from './filters';
import { RecentCasesComp } from './recent_cases';
import { FilterMode as RecentCasesFilterMode } from './types';
import { useCurrentUser } from '../../common/lib/kibana';
import { AuthenticatedElasticUser, useCurrentUser } from '../../common/lib/kibana';
import { useAllCasesNavigation } from '../../common/navigation';
import { casesQueryClient } from '../cases_context/query_client';
import { useGetCurrentUserProfile } from '../../containers/user_profiles/use_get_current_user_profile';
import { User } from '../../../common/api';

export interface RecentCasesProps {
maxCasesToShow: number;
}

const RecentCases = React.memo(({ maxCasesToShow }: RecentCasesProps) => {
const RecentCases = React.memo((props: RecentCasesProps) => {
return (
<QueryClientProvider client={casesQueryClient}>
<RecentCasesWithoutQueryProvider {...props} />
</QueryClientProvider>
);
});

RecentCases.displayName = 'RecentCases';

// eslint-disable-next-line import/no-default-export
export { RecentCases as default };

const RecentCasesWithoutQueryProvider = React.memo(({ maxCasesToShow }: RecentCasesProps) => {
const currentUser = useCurrentUser();
const { data: currentUserProfile, isLoading: isLoadingCurrentUserProfile } =
useGetCurrentUserProfile();
const { getAllCasesUrl, navigateToAllCases } = useAllCasesNavigation();

const [recentCasesFilterBy, setRecentCasesFilterBy] =
Expand All @@ -37,42 +55,36 @@ const RecentCases = React.memo(({ maxCasesToShow }: RecentCasesProps) => {
[navigateToAllCases]
);

const recentCasesFilterOptions = useMemo(
() =>
recentCasesFilterBy === 'myRecentlyReported' && currentUser != null
? {
reporters: [
{
email: currentUser.email,
full_name: currentUser.fullName,
username: currentUser.username,
},
],
}
: { reporters: [] },
[currentUser, recentCasesFilterBy]
);
const recentCasesFilterOptions = useMemo(() => {
return getReporterFilter({
currentUser,
isLoadingCurrentUserProfile,
recentCasesFilterBy,
currentUserProfile,
});
}, [currentUser, currentUserProfile, isLoadingCurrentUserProfile, recentCasesFilterBy]);

// show the recently reported if we have the current user profile, or if we have the fallback user information
const showMyRecentlyReported = currentUserProfile != null || currentUser != null;

return (
<QueryClientProvider client={casesQueryClient}>
<>
<EuiFlexGroup alignItems="center" gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<h2>{i18n.RECENT_CASES}</h2>
</EuiTitle>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<RecentCasesFilters
filterBy={recentCasesFilterBy}
setFilterBy={setRecentCasesFilterBy}
showMyRecentlyReported={currentUser != null}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
</>
<>
<EuiFlexGroup alignItems="center" gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<h2>{i18n.RECENT_CASES}</h2>
</EuiTitle>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<RecentCasesFilters
filterBy={recentCasesFilterBy}
setFilterBy={setRecentCasesFilterBy}
showMyRecentlyReported={showMyRecentlyReported}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="s" />
<EuiText color="subdued" size="s">
<RecentCasesComp filterOptions={recentCasesFilterOptions} maxCasesToShow={maxCasesToShow} />
<EuiHorizontalRule margin="s" />
Expand All @@ -83,11 +95,50 @@ const RecentCases = React.memo(({ maxCasesToShow }: RecentCasesProps) => {
</LinkAnchor>
</EuiText>
</EuiText>
</QueryClientProvider>
</>
);
});

RecentCases.displayName = 'RecentCases';
RecentCasesWithoutQueryProvider.displayName = 'RecentCases';

// eslint-disable-next-line import/no-default-export
export { RecentCases as default };
const getReporterFilter = ({
recentCasesFilterBy,
currentUserProfile,
currentUser,
isLoadingCurrentUserProfile,
}: {
recentCasesFilterBy: RecentCasesFilterMode;
currentUserProfile?: UserProfile;
currentUser: AuthenticatedElasticUser | null;
isLoadingCurrentUserProfile: boolean;
}): { reporters: User[] } => {
const emptyFilter = { reporters: [] };
if (recentCasesFilterBy !== 'myRecentlyReported') {
return emptyFilter;
}

if (currentUserProfile != null && !isLoadingCurrentUserProfile) {
return {
reporters: [
{
email: currentUserProfile.user.email,
full_name: currentUserProfile.user.full_name,
username: currentUserProfile.user.username,
profile_uid: currentUserProfile.uid,
},
],
};
} else if (currentUser != null) {
return {
reporters: [
{
email: currentUser.email,
full_name: currentUser.fullName,
username: currentUser.username,
},
],
};
}

return emptyFilter;
};
15 changes: 14 additions & 1 deletion x-pack/plugins/cases/public/containers/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type { ValidFeatureId } from '@kbn/rule-data-utils';
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/constants';
import { isEmpty } from 'lodash';
import {
Cases,
FetchCasesProps,
Expand Down Expand Up @@ -182,7 +183,7 @@ export const getCases = async ({
...(filterOptions.status !== StatusAll ? { status: filterOptions.status } : {}),
...(filterOptions.severity !== SeverityAll ? { severity: filterOptions.severity } : {}),
assignees: filterOptions.assignees,
reporters: filterOptions.reporters.map((r) => r.username ?? '').filter((r) => r !== ''),
reporters: constructReportersFilter(filterOptions.reporters),
tags: filterOptions.tags,
...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}),
...(filterOptions.searchFields.length > 0 ? { searchFields: filterOptions.searchFields } : {}),
Expand All @@ -199,6 +200,18 @@ export const getCases = async ({
return convertAllCasesToCamel(decodeCasesFindResponse(response));
};

export const constructReportersFilter = (reporters: User[]) => {
return reporters
.map((reporter) => {
if (reporter.profile_uid != null) {
return reporter.profile_uid;
}

return reporter.username ?? '';
})
.filter((reporterID) => !isEmpty(reporterID));
};

export const postCase = async (newCase: CasePostRequest, signal: AbortSignal): Promise<Case> => {
const response = await KibanaServices.get().http.fetch<CaseResponse>(CASES_URL, {
method: 'POST',
Expand Down
Loading

0 comments on commit a71f965

Please sign in to comment.