mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cases][ResponseOps] Remove userCanCrud from props (#135353)
* Starting conversion to permissions from userCanCrud * Migrating userCanCrud to context * Fixing tests * Fix type error * Removing missed userCanCrud * Fixing tests and addressing permissions.all feedback * Fixing test * Addressing observability test feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
eae262edad
commit
de9a822c2b
90 changed files with 709 additions and 531 deletions
|
@ -48,7 +48,7 @@ To initialize the `CasesContext` you can use this code:
|
|||
// somewhere high on your plugin render tree
|
||||
<CasesContext
|
||||
owner={[PLUGIN_CASES_OWNER_ID]}
|
||||
userCanCrud={CASES_USER_CAN_CRUD}
|
||||
permissions={CASES_PERMISSIONS}
|
||||
features={CASES_FEATURES}
|
||||
>
|
||||
<RouteRender /> {/* or something similar */}
|
||||
|
@ -57,11 +57,11 @@ To initialize the `CasesContext` you can use this code:
|
|||
|
||||
props:
|
||||
|
||||
| prop | type | description |
|
||||
| --------------------- | --------------- | -------------------------------------------------------------- |
|
||||
| PLUGIN_CASES_OWNER_ID | `string` | The owner string for your plugin. e.g: securitySolution |
|
||||
| CASES_USER_CAN_CRUD | `boolean` | Defines if the user has access to cases to CRUD |
|
||||
| CASES_FEATURES | `CasesFeatures` | `CasesFeatures` object defining the features to enable/disable |
|
||||
| prop | type | description |
|
||||
| --------------------- | ------------------- | -------------------------------------------------------------- |
|
||||
| PLUGIN_CASES_OWNER_ID | `string` | The owner string for your plugin. e.g: securitySolution |
|
||||
| CASES_PERMISSIONS | `CasesPermissions` | `CasesPermissions` object defining the user's permissions |
|
||||
| CASES_FEATURES | `CasesFeatures` | `CasesFeatures` object defining the features to enable/disable |
|
||||
|
||||
|
||||
### Cases UI client
|
||||
|
@ -83,7 +83,10 @@ const { cases } = useKibana().services;
|
|||
// call in the return as you would any component
|
||||
cases.getCases({
|
||||
basePath: '/investigate/cases',
|
||||
userCanCrud: true,
|
||||
permissions: {
|
||||
all: true,
|
||||
read: true,
|
||||
},
|
||||
owner: ['securitySolution'],
|
||||
features: { alerts: { sync: false }, metrics: ['alerts.count', 'lifespan'] }
|
||||
timelineIntegration: {
|
||||
|
@ -206,7 +209,7 @@ Arguments:
|
|||
|
||||
| Property | Description |
|
||||
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| userCanCrud | `boolean;` user permissions to crud |
|
||||
| permissions | `CasesPermissions` object defining the user's permissions |
|
||||
| owner | `string[];` owner ids of the cases |
|
||||
| basePath | `string;` path to mount the Cases router on top of |
|
||||
| useFetchAlertData | `(alertIds: string[]) => [boolean, Record<string, unknown>];` fetch alerts |
|
||||
|
@ -236,7 +239,7 @@ Arguments:
|
|||
|
||||
| Property | Description |
|
||||
| --------------- | ---------------------------------------------------------------------------------- |
|
||||
| userCanCrud | `boolean;` user permissions to crud |
|
||||
| permissions | `CasesPermissions` object defining the user's permissions |
|
||||
| owner | `string[];` owner ids of the cases |
|
||||
| alertData? | `Omit<CommentRequestAlertType, 'type'>;` alert data to post to case |
|
||||
| hiddenStatuses? | `CaseStatuses[];` array of hidden statuses |
|
||||
|
@ -253,7 +256,7 @@ Arguments:
|
|||
|
||||
| Property | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| userCanCrud | `boolean;` user permissions to crud |
|
||||
| permissions | `CasesPermissions` object defining the user's permissions |
|
||||
| owner | `string[];` owner ids of the cases |
|
||||
| onClose | `() => void;` callback when create case is canceled |
|
||||
| onSuccess | `(theCase: Case) => Promise<void>;` callback passing newly created case after pushCaseToExternalService is called |
|
||||
|
@ -267,11 +270,11 @@ UI component:
|
|||
|
||||
Arguments:
|
||||
|
||||
| Property | Description |
|
||||
| -------------- | ------------------------------------------- |
|
||||
| userCanCrud | `boolean;` user permissions to crud |
|
||||
| owner | `string[];` owner ids of the cases |
|
||||
| maxCasesToShow | `number;` number of cases to show in widget |
|
||||
| Property | Description |
|
||||
| -------------- | ---------------------------------------------------------- |
|
||||
| permissions | `CasesPermissions` object defining the user's permissions |
|
||||
| owner | `string[];` owner ids of the cases |
|
||||
| maxCasesToShow | `number;` number of cases to show in widget |
|
||||
|
||||
UI component:
|
||||
![Recent Cases Component][recent-cases-img]
|
||||
|
@ -289,7 +292,7 @@ Arguments:
|
|||
|
||||
| Property | Description |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| userCanCrud | `boolean;` user permissions to crud |
|
||||
| permissions | `CasesPermissions` object defining the user's permissions |
|
||||
| onClose | `() => void;` callback when create case is canceled |
|
||||
| onSuccess | `(theCase: Case) => Promise<void>;` callback passing newly created case after pushCaseToExternalService is called |
|
||||
| afterCaseCreated? | `(theCase: Case) => Promise<void>;` callback passing newly created case before pushCaseToExternalService is called |
|
||||
|
|
23
x-pack/plugins/cases/public/client/helpers/capabilities.ts
Normal file
23
x-pack/plugins/cases/public/client/helpers/capabilities.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export interface CasesPermissions {
|
||||
all: boolean;
|
||||
read: boolean;
|
||||
}
|
||||
|
||||
export const getUICapabilities = (
|
||||
featureCapabilities: Partial<Record<string, boolean | Record<string, boolean>>>
|
||||
): CasesPermissions => {
|
||||
const read = !!featureCapabilities?.read_cases;
|
||||
const all = !!featureCapabilities?.crud_cases;
|
||||
|
||||
return {
|
||||
all,
|
||||
read,
|
||||
};
|
||||
};
|
|
@ -22,12 +22,12 @@ const AllCasesSelectorModalLazy: React.FC<AllCasesSelectorModalProps> = lazy(
|
|||
export const getAllCasesSelectorModalLazy = ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
hiddenStatuses,
|
||||
onRowClick,
|
||||
onClose,
|
||||
}: GetAllCasesSelectorModalPropsInternal) => (
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, userCanCrud }}>
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, permissions }}>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<AllCasesSelectorModalLazy
|
||||
hiddenStatuses={hiddenStatuses}
|
||||
|
|
|
@ -18,7 +18,7 @@ const CasesRoutesLazy: React.FC<CasesProps> = lazy(() => import('../../component
|
|||
export const getCasesLazy = ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
basePath,
|
||||
onComponentInitialized,
|
||||
actionsNavigation,
|
||||
|
@ -34,7 +34,7 @@ export const getCasesLazy = ({
|
|||
value={{
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
basePath,
|
||||
features,
|
||||
releasePhase,
|
||||
|
|
|
@ -22,7 +22,7 @@ const CasesProviderLazy: React.FC<{ value: GetCasesContextPropsInternal }> = laz
|
|||
const CasesProviderLazyWrapper = ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
features,
|
||||
children,
|
||||
releasePhase,
|
||||
|
@ -33,7 +33,7 @@ const CasesProviderLazyWrapper = ({
|
|||
value={{
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
features,
|
||||
releasePhase,
|
||||
}}
|
||||
|
|
|
@ -22,14 +22,14 @@ export const CreateCaseFlyoutLazy: React.FC<CreateCaseFlyoutProps> = lazy(
|
|||
export const getCreateCaseFlyoutLazy = ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
features,
|
||||
afterCaseCreated,
|
||||
onClose,
|
||||
onSuccess,
|
||||
attachments,
|
||||
}: GetCreateCaseFlyoutPropsInternal) => (
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, userCanCrud, features }}>
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, permissions, features }}>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<CreateCaseFlyoutLazy
|
||||
afterCaseCreated={afterCaseCreated}
|
||||
|
|
|
@ -22,10 +22,10 @@ const RecentCasesLazy: React.FC<RecentCasesProps> = lazy(
|
|||
export const getRecentCasesLazy = ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
maxCasesToShow,
|
||||
}: GetRecentCasesPropsInternal) => (
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, userCanCrud }}>
|
||||
<CasesProvider value={{ externalReferenceAttachmentTypeRegistry, owner, permissions }}>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<RecentCasesLazy maxCasesToShow={maxCasesToShow} />
|
||||
</Suspense>
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('hooks', () => {
|
|||
|
||||
expect(result.current).toEqual({
|
||||
actions: { crud: true, read: true },
|
||||
generalCases: { crud: true, read: true },
|
||||
generalCases: { all: true, read: true },
|
||||
visualize: { crud: true, read: true },
|
||||
dashboard: { crud: true, read: true },
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { AuthenticatedUser } from '@kbn/security-plugin/common/model';
|
||||
import { NavigateToAppOptions } from '@kbn/core/public';
|
||||
import { CasesPermissions, getUICapabilities } from '../../../client/helpers/capabilities';
|
||||
import { convertToCamelCase } from '../../../api/utils';
|
||||
import {
|
||||
FEATURE_ID,
|
||||
|
@ -166,7 +167,7 @@ interface Capabilities {
|
|||
}
|
||||
interface UseApplicationCapabilities {
|
||||
actions: Capabilities;
|
||||
generalCases: Capabilities;
|
||||
generalCases: CasesPermissions;
|
||||
visualize: Capabilities;
|
||||
dashboard: Capabilities;
|
||||
}
|
||||
|
@ -179,13 +180,14 @@ interface UseApplicationCapabilities {
|
|||
export const useApplicationCapabilities = (): UseApplicationCapabilities => {
|
||||
const capabilities = useKibana().services?.application?.capabilities;
|
||||
const casesCapabilities = capabilities[FEATURE_ID];
|
||||
const permissions = getUICapabilities(casesCapabilities);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
actions: { crud: !!capabilities.actions?.save, read: !!capabilities.actions?.show },
|
||||
generalCases: {
|
||||
crud: !!casesCapabilities?.crud_cases,
|
||||
read: !!casesCapabilities?.read_cases,
|
||||
all: permissions.all,
|
||||
read: permissions.read,
|
||||
},
|
||||
visualize: { crud: !!capabilities.visualize?.save, read: !!capabilities.visualize?.show },
|
||||
dashboard: {
|
||||
|
@ -200,8 +202,8 @@ export const useApplicationCapabilities = (): UseApplicationCapabilities => {
|
|||
capabilities.dashboard?.show,
|
||||
capabilities.visualize?.save,
|
||||
capabilities.visualize?.show,
|
||||
casesCapabilities?.crud_cases,
|
||||
casesCapabilities?.read_cases,
|
||||
permissions.all,
|
||||
permissions.read,
|
||||
]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -23,13 +23,14 @@ import {
|
|||
import { FieldHook } from '../shared_imports';
|
||||
import { StartServices } from '../../types';
|
||||
import { ReleasePhase } from '../../components/types';
|
||||
import { CasesPermissions } from '../../client/helpers/capabilities';
|
||||
import { AttachmentTypeRegistry } from '../../client/attachment_framework/registry';
|
||||
import { ExternalReferenceAttachmentType } from '../../client/attachment_framework/types';
|
||||
import { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry';
|
||||
|
||||
interface TestProviderProps {
|
||||
children: React.ReactNode;
|
||||
userCanCrud?: boolean;
|
||||
permissions?: CasesPermissions;
|
||||
features?: CasesFeatures;
|
||||
owner?: string[];
|
||||
releasePhase?: ReleasePhase;
|
||||
|
@ -45,7 +46,7 @@ const TestProvidersComponent: React.FC<TestProviderProps> = ({
|
|||
children,
|
||||
features,
|
||||
owner = [SECURITY_SOLUTION_OWNER],
|
||||
userCanCrud = true,
|
||||
permissions = allCasesPermissions(),
|
||||
releasePhase = 'ga',
|
||||
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
|
||||
}) => {
|
||||
|
@ -63,7 +64,7 @@ const TestProvidersComponent: React.FC<TestProviderProps> = ({
|
|||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CasesProvider
|
||||
value={{ externalReferenceAttachmentTypeRegistry, features, owner, userCanCrud }}
|
||||
value={{ externalReferenceAttachmentTypeRegistry, features, owner, permissions }}
|
||||
>
|
||||
{children}
|
||||
</CasesProvider>
|
||||
|
@ -92,10 +93,24 @@ export const testQueryClient = new QueryClient({
|
|||
},
|
||||
});
|
||||
|
||||
export const buildCasesPermissions = (overrides: Partial<CasesPermissions> = {}) => {
|
||||
const read = overrides.read ?? true;
|
||||
const all = overrides.all ?? true;
|
||||
|
||||
return {
|
||||
all,
|
||||
read,
|
||||
};
|
||||
};
|
||||
|
||||
export const allCasesPermissions = () => buildCasesPermissions();
|
||||
export const noCasesPermissions = () => buildCasesPermissions({ read: false, all: false });
|
||||
export const readCasesPermissions = () => buildCasesPermissions({ all: false });
|
||||
|
||||
export const createAppMockRenderer = ({
|
||||
features,
|
||||
owner = [SECURITY_SOLUTION_OWNER],
|
||||
userCanCrud = true,
|
||||
permissions = allCasesPermissions(),
|
||||
releasePhase = 'ga',
|
||||
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
|
||||
}: Omit<TestProviderProps, 'children'> = {}): AppMockRenderer => {
|
||||
|
@ -118,7 +133,7 @@ export const createAppMockRenderer = ({
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
features,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
releasePhase,
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -29,7 +29,6 @@ const createAttachments = jest.fn();
|
|||
const addCommentProps: AddCommentProps = {
|
||||
id: 'newComment',
|
||||
caseId: '1234',
|
||||
userCanCrud: true,
|
||||
onCommentSaving,
|
||||
onCommentPosted,
|
||||
showLoading: false,
|
||||
|
@ -120,8 +119,8 @@ describe('AddComment ', () => {
|
|||
isLoading: true,
|
||||
}));
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<AddComment {...{ ...addCommentProps, userCanCrud: false }} />
|
||||
<TestProviders permissions={{ all: false, read: false }}>
|
||||
<AddComment {...{ ...addCommentProps }} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ export interface AddCommentRefObject {
|
|||
export interface AddCommentProps {
|
||||
id: string;
|
||||
caseId: string;
|
||||
userCanCrud?: boolean;
|
||||
onCommentSaving?: () => void;
|
||||
onCommentPosted: (newCase: Case) => void;
|
||||
showLoading?: boolean;
|
||||
|
@ -57,20 +56,12 @@ export interface AddCommentProps {
|
|||
export const AddComment = React.memo(
|
||||
forwardRef<AddCommentRefObject, AddCommentProps>(
|
||||
(
|
||||
{
|
||||
id,
|
||||
caseId,
|
||||
userCanCrud,
|
||||
onCommentPosted,
|
||||
onCommentSaving,
|
||||
showLoading = true,
|
||||
statusActionButton,
|
||||
},
|
||||
{ id, caseId, onCommentPosted, onCommentSaving, showLoading = true, statusActionButton },
|
||||
ref
|
||||
) => {
|
||||
const editorRef = useRef<EuiMarkdownEditorRef>(null);
|
||||
const [focusOnContext, setFocusOnContext] = useState(false);
|
||||
const { owner } = useCasesContext();
|
||||
const { permissions, owner } = useCasesContext();
|
||||
const { isLoading, createAttachments } = useCreateAttachments();
|
||||
|
||||
const { form } = useForm<AddCommentFormSchema>({
|
||||
|
@ -156,7 +147,7 @@ export const AddComment = React.memo(
|
|||
return (
|
||||
<span id="add-comment-permLink">
|
||||
{isLoading && showLoading && <MySpinner data-test-subj="loading-spinner" size="xl" />}
|
||||
{userCanCrud && (
|
||||
{permissions.all && (
|
||||
<Form form={form}>
|
||||
<UseField
|
||||
path={fieldName}
|
||||
|
|
|
@ -139,7 +139,6 @@ describe('AllCasesListGeneric', () => {
|
|||
handleIsLoading: jest.fn(),
|
||||
isLoadingCases: [],
|
||||
isSelectorView: false,
|
||||
userCanCrud: true,
|
||||
};
|
||||
|
||||
let appMockRenderer: AppMockRenderer;
|
||||
|
|
|
@ -61,7 +61,7 @@ export interface AllCasesListProps {
|
|||
|
||||
export const AllCasesList = React.memo<AllCasesListProps>(
|
||||
({ hiddenStatuses = [], isSelectorView = false, onRowClick, doRefresh }) => {
|
||||
const { owner, userCanCrud } = useCasesContext();
|
||||
const { owner, permissions } = useCasesContext();
|
||||
const availableSolutions = useAvailableCasesOwners();
|
||||
const [refresh, setRefresh] = useState(0);
|
||||
|
||||
|
@ -185,14 +185,13 @@ export const AllCasesList = React.memo<AllCasesListProps>(
|
|||
[deselectCases, setFilterOptions, refreshCases, setQueryParams]
|
||||
);
|
||||
|
||||
const showActions = userCanCrud && !isSelectorView;
|
||||
const showActions = permissions.all && !isSelectorView;
|
||||
|
||||
const columns = useCasesColumns({
|
||||
filterStatus: filterOptions.status ?? StatusAll,
|
||||
handleIsLoading,
|
||||
refreshCases,
|
||||
isSelectorView,
|
||||
userCanCrud,
|
||||
connectors,
|
||||
onRowClick,
|
||||
showSolutionColumn: !hasOwner && availableSolutions.length > 1,
|
||||
|
@ -271,7 +270,6 @@ export const AllCasesList = React.memo<AllCasesListProps>(
|
|||
sorting={sorting}
|
||||
tableRef={tableRef}
|
||||
tableRowProps={tableRowProps}
|
||||
userCanCrud={userCanCrud}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -43,6 +43,7 @@ import type { CasesOwners } from '../../client/helpers/can_use_cases';
|
|||
import { useCasesFeatures } from '../cases_context/use_cases_features';
|
||||
import { severities } from '../severity/config';
|
||||
import { useUpdateCase } from '../../containers/use_update_case';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
export type CasesColumns =
|
||||
| EuiTableActionsColumnType<Case>
|
||||
|
@ -61,7 +62,6 @@ export interface GetCasesColumn {
|
|||
handleIsLoading: (a: boolean) => void;
|
||||
refreshCases?: (a?: boolean) => void;
|
||||
isSelectorView: boolean;
|
||||
userCanCrud: boolean;
|
||||
connectors?: ActionConnector[];
|
||||
onRowClick?: (theCase: Case) => void;
|
||||
|
||||
|
@ -72,7 +72,6 @@ export const useCasesColumns = ({
|
|||
handleIsLoading,
|
||||
refreshCases,
|
||||
isSelectorView,
|
||||
userCanCrud,
|
||||
connectors = [],
|
||||
onRowClick,
|
||||
showSolutionColumn,
|
||||
|
@ -88,6 +87,7 @@ export const useCasesColumns = ({
|
|||
} = useDeleteCases();
|
||||
|
||||
const { isAlertsEnabled } = useCasesFeatures();
|
||||
const { permissions } = useCasesContext();
|
||||
|
||||
const [deleteThisCase, setDeleteThisCase] = useState<DeleteCase>({
|
||||
id: '',
|
||||
|
@ -319,7 +319,7 @@ export const useCasesColumns = ({
|
|||
return (
|
||||
<StatusContextMenu
|
||||
currentStatus={theCase.status}
|
||||
disabled={!userCanCrud || isLoadingUpdateCase}
|
||||
disabled={!permissions.all || isLoadingUpdateCase}
|
||||
onStatusChanged={(status) =>
|
||||
handleDispatchUpdate({
|
||||
updateKey: 'status',
|
||||
|
@ -372,7 +372,7 @@ export const useCasesColumns = ({
|
|||
},
|
||||
]
|
||||
: []),
|
||||
...(userCanCrud && !isSelectorView
|
||||
...(permissions.all && !isSelectorView
|
||||
? [
|
||||
{
|
||||
name: (
|
||||
|
|
|
@ -11,23 +11,32 @@ import { HeaderPage } from '../header_page';
|
|||
import * as i18n from './translations';
|
||||
import { ErrorMessage } from '../use_push_to_service/callout/types';
|
||||
import { NavButtons } from './nav_buttons';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
interface OwnProps {
|
||||
actionsErrors: ErrorMessage[];
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
|
||||
export const CasesTableHeader: FunctionComponent<Props> = ({ actionsErrors, userCanCrud }) => (
|
||||
<HeaderPage title={i18n.PAGE_TITLE} border data-test-subj="cases-all-title">
|
||||
<EuiFlexGroup alignItems="center" gutterSize="m" wrap={true} data-test-subj="all-cases-header">
|
||||
{userCanCrud ? (
|
||||
<EuiFlexItem>
|
||||
<NavButtons actionsErrors={actionsErrors} />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</HeaderPage>
|
||||
);
|
||||
export const CasesTableHeader: FunctionComponent<Props> = ({ actionsErrors }) => {
|
||||
const { permissions } = useCasesContext();
|
||||
|
||||
return (
|
||||
<HeaderPage title={i18n.PAGE_TITLE} border data-test-subj="cases-all-title">
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="m"
|
||||
wrap={true}
|
||||
data-test-subj="all-cases-header"
|
||||
>
|
||||
{permissions.all ? (
|
||||
<EuiFlexItem>
|
||||
<NavButtons actionsErrors={actionsErrors} />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</HeaderPage>
|
||||
);
|
||||
};
|
||||
CasesTableHeader.displayName = 'CasesTableHeader';
|
||||
|
|
|
@ -8,14 +8,12 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { CasesDeepLinkId } from '../../common/navigation';
|
||||
import { useGetActionLicense } from '../../containers/use_get_action_license';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
import { useCasesBreadcrumbs } from '../use_breadcrumbs';
|
||||
import { getActionLicenseError } from '../use_push_to_service/helpers';
|
||||
import { AllCasesList } from './all_cases_list';
|
||||
import { CasesTableHeader } from './header';
|
||||
|
||||
export const AllCases: React.FC = () => {
|
||||
const { userCanCrud } = useCasesContext();
|
||||
useCasesBreadcrumbs(CasesDeepLinkId.cases);
|
||||
|
||||
const { data: actionLicense = null } = useGetActionLicense();
|
||||
|
@ -23,7 +21,7 @@ export const AllCases: React.FC = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CasesTableHeader actionsErrors={actionsErrors} userCanCrud={userCanCrud} />
|
||||
<CasesTableHeader actionsErrors={actionsErrors} />
|
||||
<AllCasesList />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -61,7 +61,10 @@ describe('use cases add to existing case modal hook', () => {
|
|||
value={{
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner: ['test'],
|
||||
userCanCrud: true,
|
||||
permissions: {
|
||||
all: true,
|
||||
read: true,
|
||||
},
|
||||
appId: 'test',
|
||||
appTitle: 'jest',
|
||||
basePath: '/jest',
|
||||
|
|
|
@ -22,6 +22,7 @@ import { LinkButton } from '../links';
|
|||
import { Cases, Case, FilterOptions } from '../../../common/ui/types';
|
||||
import * as i18n from './translations';
|
||||
import { useCreateCaseNavigation } from '../../common/navigation';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
interface CasesTableProps {
|
||||
columns: EuiBasicTableProps<Case>['columns'];
|
||||
|
@ -42,7 +43,6 @@ interface CasesTableProps {
|
|||
sorting: EuiBasicTableProps<Case>['sorting'];
|
||||
tableRef: MutableRefObject<EuiBasicTable | null>;
|
||||
tableRowProps: EuiBasicTableProps<Case>['rowProps'];
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
const Div = styled.div`
|
||||
|
@ -68,8 +68,8 @@ export const CasesTable: FunctionComponent<CasesTableProps> = ({
|
|||
sorting,
|
||||
tableRef,
|
||||
tableRowProps,
|
||||
userCanCrud,
|
||||
}) => {
|
||||
const { permissions } = useCasesContext();
|
||||
const { getCreateCaseUrl, navigateToCreateCase } = useCreateCaseNavigation();
|
||||
const navigateToCreateCaseClick = useCallback(
|
||||
(ev) => {
|
||||
|
@ -109,11 +109,11 @@ export const CasesTable: FunctionComponent<CasesTableProps> = ({
|
|||
<EuiEmptyPrompt
|
||||
title={<h3>{i18n.NO_CASES}</h3>}
|
||||
titleSize="xs"
|
||||
body={userCanCrud ? i18n.NO_CASES_BODY : i18n.NO_CASES_BODY_READ_ONLY}
|
||||
body={permissions.all ? i18n.NO_CASES_BODY : i18n.NO_CASES_BODY_READ_ONLY}
|
||||
actions={
|
||||
userCanCrud && (
|
||||
permissions.all && (
|
||||
<LinkButton
|
||||
isDisabled={!userCanCrud}
|
||||
isDisabled={!permissions.all}
|
||||
fill
|
||||
size="s"
|
||||
onClick={navigateToCreateCaseClick}
|
||||
|
|
|
@ -31,7 +31,7 @@ const CasesAppComponent: React.FC<CasesAppProps> = ({
|
|||
externalReferenceAttachmentTypeRegistry,
|
||||
owner: [APP_OWNER],
|
||||
useFetchAlertData: () => [false, {}],
|
||||
userCanCrud: userCapabilities.generalCases.crud,
|
||||
permissions: userCapabilities.generalCases,
|
||||
basePath: '/',
|
||||
features: { alerts: { enabled: false } },
|
||||
releasePhase: 'experimental',
|
||||
|
|
|
@ -10,8 +10,9 @@ import React from 'react';
|
|||
import { MemoryRouterProps } from 'react-router';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { readCasesPermissions, TestProviders } from '../../common/mock';
|
||||
import { CasesRoutes } from './routes';
|
||||
import { CasesPermissions } from '../../client/helpers/capabilities';
|
||||
|
||||
jest.mock('../all_cases', () => ({
|
||||
AllCases: () => <div>{'All cases'}</div>,
|
||||
|
@ -29,10 +30,10 @@ const getCaseViewPaths = () => ['/cases/test-id', '/cases/test-id/comment-id'];
|
|||
|
||||
const renderWithRouter = (
|
||||
initialEntries: MemoryRouterProps['initialEntries'] = ['/cases'],
|
||||
userCanCrud = true
|
||||
permissions?: CasesPermissions
|
||||
) => {
|
||||
return render(
|
||||
<TestProviders userCanCrud={userCanCrud}>
|
||||
<TestProviders permissions={permissions}>
|
||||
<MemoryRouter initialEntries={initialEntries}>
|
||||
<CasesRoutes useFetchAlertData={(alertIds) => [false, {}]} />
|
||||
</MemoryRouter>
|
||||
|
@ -48,8 +49,8 @@ describe('Cases routes', () => {
|
|||
});
|
||||
|
||||
// User has read only privileges
|
||||
it('user can navigate to the all cases page with userCanCrud = false', () => {
|
||||
renderWithRouter(['/cases'], false);
|
||||
it('user can navigate to the all cases page with only read permissions', () => {
|
||||
renderWithRouter(['/cases'], readCasesPermissions());
|
||||
expect(screen.getByText('All cases')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -68,9 +69,9 @@ describe('Cases routes', () => {
|
|||
);
|
||||
|
||||
it.each(getCaseViewPaths())(
|
||||
'user can navigate to the cases view page with userCanCrud = false and path: %s',
|
||||
'user can navigate to the cases view page with read permissions and path: %s',
|
||||
async (path: string) => {
|
||||
renderWithRouter([path], false);
|
||||
renderWithRouter([path], readCasesPermissions());
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('case-view-loading')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -84,8 +85,8 @@ describe('Cases routes', () => {
|
|||
expect(screen.getByText('Create case')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the no privileges page if userCanCrud = false', () => {
|
||||
renderWithRouter(['/cases/create'], false);
|
||||
it('shows the no privileges page if user is read only', () => {
|
||||
renderWithRouter(['/cases/create'], readCasesPermissions());
|
||||
expect(screen.getByText('Privileges required')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -96,8 +97,8 @@ describe('Cases routes', () => {
|
|||
expect(screen.getByText('Configure cases')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the no privileges page if userCanCrud = false', () => {
|
||||
renderWithRouter(['/cases/configure'], false);
|
||||
it('shows the no privileges page if user is read only', () => {
|
||||
renderWithRouter(['/cases/configure'], readCasesPermissions());
|
||||
expect(screen.getByText('Privileges required')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,7 +40,7 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
|
|||
refreshRef,
|
||||
timelineIntegration,
|
||||
}) => {
|
||||
const { basePath, userCanCrud } = useCasesContext();
|
||||
const { basePath, permissions } = useCasesContext();
|
||||
const { navigateToAllCases } = useAllCasesNavigation();
|
||||
const { navigateToCaseView } = useCaseViewNavigation();
|
||||
useReadonlyHeader();
|
||||
|
@ -58,7 +58,7 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
|
|||
</Route>
|
||||
|
||||
<Route path={getCreateCasePath(basePath)}>
|
||||
{userCanCrud ? (
|
||||
{permissions.all ? (
|
||||
<CreateCase
|
||||
onSuccess={onCreateCaseSuccess}
|
||||
onCancel={navigateToAllCases}
|
||||
|
@ -70,7 +70,7 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
|
|||
</Route>
|
||||
|
||||
<Route path={getCasesConfigurePath(basePath)}>
|
||||
{userCanCrud ? (
|
||||
{permissions.all ? (
|
||||
<ConfigureCases />
|
||||
) : (
|
||||
<NoPrivilegesPage pageName={i18n.CONFIGURE_CASES_PAGE_NAME} />
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { useKibana } from '../../common/lib/kibana';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { readCasesPermissions, TestProviders } from '../../common/mock';
|
||||
import { useReadonlyHeader } from './use_readonly_header';
|
||||
|
||||
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
|
||||
|
@ -33,7 +33,9 @@ describe('CaseContainerComponent', () => {
|
|||
|
||||
it('displays the readonly glasses badge read permissions but not write', () => {
|
||||
renderHook(() => useReadonlyHeader(), {
|
||||
wrapper: ({ children }) => <TestProviders userCanCrud={false}>{children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}>{children}</TestProviders>
|
||||
),
|
||||
});
|
||||
|
||||
expect(mockedSetBadge).toBeCalledTimes(1);
|
||||
|
|
|
@ -15,19 +15,19 @@ import { useCasesContext } from '../cases_context/use_cases_context';
|
|||
* This component places a read-only icon badge in the header if user only has read permissions
|
||||
*/
|
||||
export function useReadonlyHeader() {
|
||||
const { userCanCrud } = useCasesContext();
|
||||
const { permissions } = useCasesContext();
|
||||
const chrome = useKibana().services.chrome;
|
||||
|
||||
// if the user is read only then display the glasses badge in the global navigation header
|
||||
const setBadge = useCallback(() => {
|
||||
if (!userCanCrud) {
|
||||
if (!permissions.all && permissions.read) {
|
||||
chrome.setBadge({
|
||||
text: i18n.READ_ONLY_BADGE_TEXT,
|
||||
tooltip: i18n.READ_ONLY_BADGE_TOOLTIP,
|
||||
iconType: 'glasses',
|
||||
});
|
||||
}
|
||||
}, [chrome, userCanCrud]);
|
||||
}, [chrome, permissions]);
|
||||
|
||||
useEffect(() => {
|
||||
setBadge();
|
||||
|
|
|
@ -42,7 +42,6 @@ describe('CaseActionBar', () => {
|
|||
isLoading: false,
|
||||
onUpdateField,
|
||||
currentExternalIncident: null,
|
||||
userCanCrud: true,
|
||||
metricsFeatures: [],
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import { useCasesFeatures } from '../cases_context/use_cases_features';
|
|||
import { FormattedRelativePreferenceDate } from '../formatted_date';
|
||||
import { getStatusDate, getStatusTitle } from './helpers';
|
||||
import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
const MyDescriptionList = styled(EuiDescriptionList)`
|
||||
${({ theme }) => css`
|
||||
|
@ -45,16 +46,15 @@ const MyDescriptionList = styled(EuiDescriptionList)`
|
|||
|
||||
export interface CaseActionBarProps {
|
||||
caseData: Case;
|
||||
userCanCrud: boolean;
|
||||
isLoading: boolean;
|
||||
onUpdateField: (args: OnUpdateFields) => void;
|
||||
}
|
||||
const CaseActionBarComponent: React.FC<CaseActionBarProps> = ({
|
||||
caseData,
|
||||
userCanCrud,
|
||||
isLoading,
|
||||
onUpdateField,
|
||||
}) => {
|
||||
const { permissions } = useCasesContext();
|
||||
const { isSyncAlertsEnabled, metricsFeatures } = useCasesFeatures();
|
||||
const date = useMemo(() => getStatusDate(caseData), [caseData]);
|
||||
const title = useMemo(() => getStatusTitle(caseData.status), [caseData.status]);
|
||||
|
@ -107,7 +107,7 @@ const CaseActionBarComponent: React.FC<CaseActionBarProps> = ({
|
|||
<EuiDescriptionListDescription>
|
||||
<StatusContextMenu
|
||||
currentStatus={caseData.status}
|
||||
disabled={!userCanCrud || isLoading}
|
||||
disabled={!permissions.all || isLoading}
|
||||
onStatusChanged={onStatusChanged}
|
||||
/>
|
||||
</EuiDescriptionListDescription>
|
||||
|
@ -134,7 +134,7 @@ const CaseActionBarComponent: React.FC<CaseActionBarProps> = ({
|
|||
responsive={false}
|
||||
justifyContent="spaceBetween"
|
||||
>
|
||||
{userCanCrud && isSyncAlertsEnabled && (
|
||||
{permissions.all && isSyncAlertsEnabled && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiDescriptionListTitle>
|
||||
<EuiFlexGroup
|
||||
|
@ -172,7 +172,7 @@ const CaseActionBarComponent: React.FC<CaseActionBarProps> = ({
|
|||
</EuiButtonEmpty>
|
||||
</span>
|
||||
</EuiFlexItem>
|
||||
{userCanCrud && (
|
||||
{permissions.all && (
|
||||
<EuiFlexItem grow={false} data-test-subj="case-view-actions">
|
||||
<Actions
|
||||
caseData={caseData}
|
||||
|
|
|
@ -40,7 +40,7 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
|
|||
showAlertDetails,
|
||||
useFetchAlertData,
|
||||
}) => {
|
||||
const { userCanCrud, features } = useCasesContext();
|
||||
const { features } = useCasesContext();
|
||||
const { navigateToCaseView } = useCaseViewNavigation();
|
||||
const { urlParams } = useUrlParams();
|
||||
const refreshCaseViewPage = useRefreshCaseViewPage();
|
||||
|
@ -171,7 +171,6 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
|
|||
data-test-subj="case-view-title"
|
||||
titleNode={
|
||||
<EditableTitle
|
||||
userCanCrud={userCanCrud}
|
||||
isLoading={isLoading && loadingKey === 'title'}
|
||||
title={caseData.title}
|
||||
onSubmit={onSubmitTitle}
|
||||
|
@ -181,7 +180,6 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
|
|||
>
|
||||
<CaseActionBar
|
||||
caseData={caseData}
|
||||
userCanCrud={userCanCrud}
|
||||
isLoading={isLoading && (loadingKey === 'status' || loadingKey === 'settings')}
|
||||
onUpdateField={onUpdateField}
|
||||
/>
|
||||
|
|
|
@ -39,7 +39,7 @@ export const CaseViewActivity = ({
|
|||
showAlertDetails?: (alertId: string, index: string) => void;
|
||||
useFetchAlertData: UseFetchAlertData;
|
||||
}) => {
|
||||
const { userCanCrud } = useCasesContext();
|
||||
const { permissions } = useCasesContext();
|
||||
const { getCaseViewUrl } = useCaseViewNavigation();
|
||||
|
||||
const { data: userActionsData, isLoading: isLoadingUserActions } = useGetCaseUserActions(
|
||||
|
@ -133,7 +133,7 @@ export const CaseViewActivity = ({
|
|||
onShowAlertDetails={onShowAlertDetails}
|
||||
onUpdateField={onUpdateField}
|
||||
statusActionButton={
|
||||
userCanCrud ? (
|
||||
permissions.all ? (
|
||||
<StatusActionButton
|
||||
status={caseData.status}
|
||||
onStatusChanged={changeStatus}
|
||||
|
@ -142,7 +142,6 @@ export const CaseViewActivity = ({
|
|||
) : null
|
||||
}
|
||||
useFetchAlertData={useFetchAlertData}
|
||||
userCanCrud={userCanCrud}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -150,7 +149,7 @@ export const CaseViewActivity = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<SeveritySidebarSelector
|
||||
isDisabled={!userCanCrud}
|
||||
isDisabled={!permissions.all}
|
||||
isLoading={isLoading}
|
||||
selectedSeverity={caseData.severity}
|
||||
onSeverityChange={onUpdateSeverity}
|
||||
|
@ -171,7 +170,6 @@ export const CaseViewActivity = ({
|
|||
/>
|
||||
) : null}
|
||||
<TagList
|
||||
userCanCrud={userCanCrud}
|
||||
tags={caseData.tags}
|
||||
onSubmit={onSubmitTags}
|
||||
isLoading={isLoading && loadingKey === 'tags'}
|
||||
|
@ -182,12 +180,11 @@ export const CaseViewActivity = ({
|
|||
caseServices={userActionsData.caseServices}
|
||||
connectorName={connectorName}
|
||||
connectors={connectors}
|
||||
hasDataToPush={userActionsData.hasDataToPush && userCanCrud}
|
||||
hasDataToPush={userActionsData.hasDataToPush}
|
||||
isLoading={isLoadingConnectors || (isLoading && loadingKey === 'connector')}
|
||||
isValidConnector={isLoadingConnectors ? true : isValidConnector}
|
||||
onSubmit={onSubmitConnector}
|
||||
userActions={userActionsData.caseUserActions}
|
||||
userCanCrud={userCanCrud}
|
||||
/>
|
||||
) : null}
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -189,7 +189,6 @@ describe('CaseView', () => {
|
|||
onComponentInitialized: jest.fn(),
|
||||
showAlertDetails: jest.fn(),
|
||||
useFetchAlertData: jest.fn().mockReturnValue([false, alertsHit[0]]),
|
||||
userCanCrud: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import React, { useState, useEffect, useReducer, Dispatch } from 'react';
|
||||
import { merge } from 'lodash';
|
||||
import useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect';
|
||||
import { DEFAULT_FEATURES } from '../../../common/constants';
|
||||
import { DEFAULT_BASE_PATH } from '../../common/navigation';
|
||||
import { useApplication } from './use_application';
|
||||
|
@ -27,7 +28,10 @@ export interface CasesContextValue {
|
|||
owner: string[];
|
||||
appId: string;
|
||||
appTitle: string;
|
||||
userCanCrud: boolean;
|
||||
permissions: {
|
||||
all: boolean;
|
||||
read: boolean;
|
||||
};
|
||||
basePath: string;
|
||||
features: CasesFeaturesAllRequired;
|
||||
releasePhase: ReleasePhase;
|
||||
|
@ -37,7 +41,7 @@ export interface CasesContextValue {
|
|||
export interface CasesContextProps
|
||||
extends Pick<
|
||||
CasesContextValue,
|
||||
'owner' | 'userCanCrud' | 'externalReferenceAttachmentTypeRegistry'
|
||||
'owner' | 'permissions' | 'externalReferenceAttachmentTypeRegistry'
|
||||
> {
|
||||
basePath?: string;
|
||||
features?: CasesFeatures;
|
||||
|
@ -56,7 +60,7 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({
|
|||
value: {
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
basePath = DEFAULT_BASE_PATH,
|
||||
features = {},
|
||||
releasePhase = 'ga',
|
||||
|
@ -67,7 +71,7 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({
|
|||
const [value, setValue] = useState<CasesContextStateValue>(() => ({
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner,
|
||||
userCanCrud,
|
||||
permissions,
|
||||
basePath,
|
||||
/**
|
||||
* The empty object at the beginning avoids the mutation
|
||||
|
@ -83,7 +87,14 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({
|
|||
}));
|
||||
|
||||
/**
|
||||
* `userCanCrud` prop may change by the parent plugin.
|
||||
* Only update the context if the nested permissions fields changed, this avoids a rerender when the object's reference
|
||||
* changes.
|
||||
*/
|
||||
useDeepCompareEffect(() => {
|
||||
setValue((prev) => ({ ...prev, permissions }));
|
||||
}, [permissions]);
|
||||
|
||||
/**
|
||||
* `appId` and `appTitle` are dynamically retrieved from kibana context.
|
||||
* We need to update the state if any of these values change, the rest of props are never updated.
|
||||
*/
|
||||
|
@ -93,10 +104,9 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({
|
|||
...prev,
|
||||
appId,
|
||||
appTitle,
|
||||
userCanCrud,
|
||||
}));
|
||||
}
|
||||
}, [appTitle, appId, userCanCrud]);
|
||||
}, [appTitle, appId]);
|
||||
|
||||
return isCasesContextValue(value) ? (
|
||||
<CasesContext.Provider value={value}>
|
||||
|
@ -108,7 +118,7 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({
|
|||
CasesProvider.displayName = 'CasesProvider';
|
||||
|
||||
function isCasesContextValue(value: CasesContextStateValue): value is CasesContextValue {
|
||||
return value.appId != null && value.appTitle != null && value.userCanCrud != null;
|
||||
return value.appId != null && value.appTitle != null && value.permissions != null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ReactWrapper, mount } from 'enzyme';
|
|||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import { ConfigureCases } from '.';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { noCasesPermissions, TestProviders } from '../../common/mock';
|
||||
import { Connectors } from './connectors';
|
||||
import { ClosureOptions } from './closure_options';
|
||||
|
||||
|
@ -191,7 +191,7 @@ describe('ConfigureCases', () => {
|
|||
test('it disables correctly when the user cannot crud', () => {
|
||||
const newWrapper = mount(<ConfigureCases />, {
|
||||
wrappingComponent: TestProviders,
|
||||
wrappingComponentProps: { userCanCrud: false },
|
||||
wrappingComponentProps: { permissions: noCasesPermissions() },
|
||||
});
|
||||
|
||||
expect(newWrapper.find('button[data-test-subj="dropdown-connectors"]').prop('disabled')).toBe(
|
||||
|
|
|
@ -51,7 +51,7 @@ const FormWrapper = styled.div`
|
|||
`;
|
||||
|
||||
export const ConfigureCases: React.FC = React.memo(() => {
|
||||
const { userCanCrud } = useCasesContext();
|
||||
const { permissions } = useCasesContext();
|
||||
const { triggersActionsUi } = useKibana().services;
|
||||
useCasesBreadcrumbs(CasesDeepLinkId.casesConfigure);
|
||||
|
||||
|
@ -225,7 +225,7 @@ export const ConfigureCases: React.FC = React.memo(() => {
|
|||
<SectionWrapper>
|
||||
<ClosureOptions
|
||||
closureTypeSelected={closureType}
|
||||
disabled={persistLoading || isLoadingConnectors || !userCanCrud}
|
||||
disabled={persistLoading || isLoadingConnectors || !permissions.all}
|
||||
onChangeClosureType={onChangeClosureType}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
|
@ -233,13 +233,13 @@ export const ConfigureCases: React.FC = React.memo(() => {
|
|||
<Connectors
|
||||
actionTypes={actionTypes}
|
||||
connectors={connectors ?? []}
|
||||
disabled={persistLoading || isLoadingConnectors || !userCanCrud}
|
||||
disabled={persistLoading || isLoadingConnectors || !permissions.all}
|
||||
handleShowEditFlyout={onClickUpdateConnector}
|
||||
isLoading={isLoadingAny}
|
||||
mappings={mappings}
|
||||
onChangeConnector={onChangeConnector}
|
||||
selectedConnector={connector}
|
||||
updateConnectorDisabled={updateConnectorDisabled || !userCanCrud}
|
||||
updateConnectorDisabled={updateConnectorDisabled || !permissions.all}
|
||||
/>
|
||||
</SectionWrapper>
|
||||
{ConnectorAddFlyout}
|
||||
|
|
|
@ -13,6 +13,7 @@ import React from 'react';
|
|||
import { CasesContext } from '../../cases_context';
|
||||
import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer';
|
||||
import { useCasesAddToNewCaseFlyout } from './use_cases_add_to_new_case_flyout';
|
||||
import { allCasesPermissions } from '../../../common/mock';
|
||||
import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry';
|
||||
|
||||
jest.mock('../../../common/use_cases_toast');
|
||||
|
@ -30,7 +31,7 @@ describe('use cases add to new case flyout hook', () => {
|
|||
value={{
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
owner: ['test'],
|
||||
userCanCrud: true,
|
||||
permissions: allCasesPermissions(),
|
||||
appId: 'test',
|
||||
appTitle: 'jest',
|
||||
basePath: '/jest',
|
||||
|
|
|
@ -11,7 +11,12 @@ import { render, waitFor, screen } from '@testing-library/react';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { EditConnector, EditConnectorProps } from '.';
|
||||
import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock';
|
||||
import {
|
||||
AppMockRenderer,
|
||||
createAppMockRenderer,
|
||||
readCasesPermissions,
|
||||
TestProviders,
|
||||
} from '../../common/mock';
|
||||
import { basicCase, basicPush, caseUserActions, connectorsMock } from '../../containers/mock';
|
||||
import { CaseConnector } from '../../containers/configure/types';
|
||||
|
||||
|
@ -36,7 +41,6 @@ const getDefaultProps = (): EditConnectorProps => {
|
|||
isValidConnector: true,
|
||||
onSubmit,
|
||||
userActions: caseUserActions,
|
||||
userCanCrud: true,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -201,11 +205,9 @@ describe('EditConnector ', () => {
|
|||
});
|
||||
|
||||
it('does not allow the connector to be edited when the user does not have write permissions', async () => {
|
||||
const defaultProps = getDefaultProps();
|
||||
const props = { ...defaultProps, userCanCrud: false };
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EditConnector {...props} />
|
||||
<TestProviders permissions={readCasesPermissions()}>
|
||||
<EditConnector {...getDefaultProps()} />
|
||||
</TestProviders>
|
||||
);
|
||||
await waitFor(() =>
|
||||
|
|
|
@ -32,6 +32,7 @@ import { getConnectorById, getConnectorsFormValidators } from '../utils';
|
|||
import { usePushToService } from '../use_push_to_service';
|
||||
import { CaseServices } from '../../containers/use_get_case_user_actions';
|
||||
import { useApplicationCapabilities } from '../../common/lib/kibana';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
export interface EditConnectorProps {
|
||||
caseData: Case;
|
||||
|
@ -48,7 +49,6 @@ export interface EditConnectorProps {
|
|||
onSuccess: () => void
|
||||
) => void;
|
||||
userActions: CaseUserActions[];
|
||||
userCanCrud?: boolean;
|
||||
}
|
||||
|
||||
const MyFlexGroup = styled(EuiFlexGroup)`
|
||||
|
@ -119,8 +119,8 @@ export const EditConnector = React.memo(
|
|||
isValidConnector,
|
||||
onSubmit,
|
||||
userActions,
|
||||
userCanCrud = true,
|
||||
}: EditConnectorProps) => {
|
||||
const { permissions } = useCasesContext();
|
||||
const caseFields = caseData.connector.fields;
|
||||
const selectedConnector = caseData.connector.id;
|
||||
|
||||
|
@ -273,7 +273,6 @@ export const EditConnector = React.memo(
|
|||
connectors,
|
||||
hasDataToPush,
|
||||
onEditClick,
|
||||
userCanCrud,
|
||||
isValidConnector,
|
||||
});
|
||||
|
||||
|
@ -289,7 +288,7 @@ export const EditConnector = React.memo(
|
|||
<h4>{i18n.CONNECTORS}</h4>
|
||||
</EuiFlexItem>
|
||||
{isLoading && <EuiLoadingSpinner data-test-subj="connector-loading" />}
|
||||
{!isLoading && !editConnector && userCanCrud && actionsReadCapabilities && (
|
||||
{!isLoading && !editConnector && permissions.all && actionsReadCapabilities && (
|
||||
<EuiFlexItem data-test-subj="connector-edit" grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="connector-edit-button"
|
||||
|
@ -317,7 +316,7 @@ export const EditConnector = React.memo(
|
|||
connectors,
|
||||
dataTestSubj: 'caseConnectors',
|
||||
defaultValue: selectedConnector,
|
||||
disabled: !userCanCrud,
|
||||
disabled: !permissions.all,
|
||||
idAria: 'caseConnectors',
|
||||
isEdit: editConnector,
|
||||
isLoading,
|
||||
|
@ -373,7 +372,7 @@ export const EditConnector = React.memo(
|
|||
{pushCallouts == null &&
|
||||
!isLoading &&
|
||||
!editConnector &&
|
||||
userCanCrud &&
|
||||
permissions.all &&
|
||||
actionsReadCapabilities && (
|
||||
<EuiFlexItem data-test-subj="has-data-to-push-button" grow={false}>
|
||||
<span>{pushButton}</span>
|
||||
|
|
|
@ -41,7 +41,10 @@ exports[`EditableTitle renders 1`] = `
|
|||
"owner": Array [
|
||||
"securitySolution",
|
||||
],
|
||||
"userCanCrud": true,
|
||||
"permissions": Object {
|
||||
"all": true,
|
||||
"read": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
|
@ -49,7 +52,6 @@ exports[`EditableTitle renders 1`] = `
|
|||
isLoading={false}
|
||||
onSubmit={[MockFunction]}
|
||||
title="Test title"
|
||||
userCanCrud={true}
|
||||
/>
|
||||
</CasesProvider>
|
||||
</QueryClientProvider>
|
||||
|
|
|
@ -41,7 +41,10 @@ exports[`HeaderPage it renders 1`] = `
|
|||
"owner": Array [
|
||||
"securitySolution",
|
||||
],
|
||||
"userCanCrud": true,
|
||||
"permissions": Object {
|
||||
"all": true,
|
||||
"read": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
|
|
|
@ -9,7 +9,12 @@ import { shallow } from 'enzyme';
|
|||
import React from 'react';
|
||||
|
||||
import '../../common/mock/match_media';
|
||||
import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock';
|
||||
import {
|
||||
AppMockRenderer,
|
||||
createAppMockRenderer,
|
||||
readCasesPermissions,
|
||||
TestProviders,
|
||||
} from '../../common/mock';
|
||||
import { EditableTitle, EditableTitleProps } from './editable_title';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
|
@ -20,7 +25,6 @@ describe('EditableTitle', () => {
|
|||
title: 'Test title',
|
||||
onSubmit: submitTitle,
|
||||
isLoading: false,
|
||||
userCanCrud: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -39,8 +43,8 @@ describe('EditableTitle', () => {
|
|||
|
||||
it('does not show the edit icon when the user does not have edit permissions', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EditableTitle {...{ ...defaultProps, userCanCrud: false }} />
|
||||
<TestProviders permissions={readCasesPermissions()}>
|
||||
<EditableTitle {...defaultProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
|
|
@ -37,19 +37,13 @@ const MySpinner = styled(EuiLoadingSpinner)`
|
|||
`;
|
||||
|
||||
export interface EditableTitleProps {
|
||||
userCanCrud: boolean;
|
||||
isLoading: boolean;
|
||||
title: string;
|
||||
onSubmit: (title: string) => void;
|
||||
}
|
||||
|
||||
const EditableTitleComponent: React.FC<EditableTitleProps> = ({
|
||||
userCanCrud = false,
|
||||
onSubmit,
|
||||
isLoading,
|
||||
title,
|
||||
}) => {
|
||||
const { releasePhase } = useCasesContext();
|
||||
const EditableTitleComponent: React.FC<EditableTitleProps> = ({ onSubmit, isLoading, title }) => {
|
||||
const { releasePhase, permissions } = useCasesContext();
|
||||
const [editMode, setEditMode] = useState(false);
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
const [newTitle, setNewTitle] = useState<string>(title);
|
||||
|
@ -124,7 +118,7 @@ const EditableTitleComponent: React.FC<EditableTitleProps> = ({
|
|||
) : (
|
||||
<Title title={title} releasePhase={releasePhase}>
|
||||
{isLoading && <MySpinner data-test-subj="editable-title-loading" />}
|
||||
{!isLoading && userCanCrud && (
|
||||
{!isLoading && permissions.all && (
|
||||
<MyEuiButtonIcon
|
||||
aria-label={i18n.EDIT_TITLE_ARIA(title as string)}
|
||||
iconType="pencil"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { TestProviders } from '../../../common/mock';
|
||||
import { readCasesPermissions, TestProviders } from '../../../common/mock';
|
||||
import { NoCases } from '.';
|
||||
|
||||
jest.mock('../../../common/navigation/hooks');
|
||||
|
@ -27,7 +27,7 @@ describe('NoCases', () => {
|
|||
|
||||
it('displays a message without a link to create a case when the user does not have write permissions', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders userCanCrud={false}>
|
||||
<TestProviders permissions={readCasesPermissions()}>
|
||||
<NoCases />
|
||||
</TestProviders>
|
||||
);
|
||||
|
|
|
@ -13,7 +13,7 @@ import { useCasesContext } from '../../cases_context/use_cases_context';
|
|||
import { useCreateCaseNavigation } from '../../../common/navigation';
|
||||
|
||||
const NoCasesComponent = () => {
|
||||
const { userCanCrud } = useCasesContext();
|
||||
const { permissions } = useCasesContext();
|
||||
const { getCreateCaseUrl, navigateToCreateCase } = useCreateCaseNavigation();
|
||||
|
||||
const navigateToCreateCaseClick = useCallback(
|
||||
|
@ -24,7 +24,7 @@ const NoCasesComponent = () => {
|
|||
[navigateToCreateCase]
|
||||
);
|
||||
|
||||
return userCanCrud ? (
|
||||
return permissions.all ? (
|
||||
<>
|
||||
<span>{i18n.NO_CASES}</span>
|
||||
<LinkAnchor
|
||||
|
|
|
@ -10,7 +10,7 @@ import { mount } from 'enzyme';
|
|||
|
||||
import { TagList, TagListProps } from '.';
|
||||
import { getFormMock } from '../__mock__/form';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { readCasesPermissions, TestProviders } from '../../common/mock';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { useForm } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib/hooks/use_form';
|
||||
import { useGetTags } from '../../containers/use_get_tags';
|
||||
|
@ -33,7 +33,6 @@ jest.mock('@elastic/eui', () => {
|
|||
});
|
||||
const onSubmit = jest.fn();
|
||||
const defaultProps: TagListProps = {
|
||||
userCanCrud: true,
|
||||
isLoading: false,
|
||||
onSubmit,
|
||||
tags: [],
|
||||
|
@ -109,10 +108,9 @@ describe('TagList ', () => {
|
|||
});
|
||||
|
||||
it('does not render when the user does not have write permissions', () => {
|
||||
const props = { ...defaultProps, userCanCrud: false };
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<TagList {...props} />
|
||||
<TestProviders permissions={readCasesPermissions()}>
|
||||
<TagList {...defaultProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(wrapper.find(`[data-test-subj="tag-list-edit"]`).exists()).toBeFalsy();
|
||||
|
|
|
@ -24,11 +24,11 @@ import { schema } from './schema';
|
|||
import { useGetTags } from '../../containers/use_get_tags';
|
||||
|
||||
import { Tags } from './tags';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
const CommonUseField = getUseField({ component: Field });
|
||||
|
||||
export interface TagListProps {
|
||||
userCanCrud?: boolean;
|
||||
isLoading: boolean;
|
||||
onSubmit: (a: string[]) => void;
|
||||
tags: string[];
|
||||
|
@ -55,144 +55,143 @@ const ColumnFlexGroup = styled(EuiFlexGroup)`
|
|||
`}
|
||||
`;
|
||||
|
||||
export const TagList = React.memo(
|
||||
({ userCanCrud = true, isLoading, onSubmit, tags }: TagListProps) => {
|
||||
const initialState = { tags };
|
||||
const { form } = useForm({
|
||||
defaultValue: initialState,
|
||||
options: { stripEmptyFields: false },
|
||||
schema,
|
||||
});
|
||||
const { submit } = form;
|
||||
const [isEditTags, setIsEditTags] = useState(false);
|
||||
export const TagList = React.memo(({ isLoading, onSubmit, tags }: TagListProps) => {
|
||||
const { permissions } = useCasesContext();
|
||||
const initialState = { tags };
|
||||
const { form } = useForm({
|
||||
defaultValue: initialState,
|
||||
options: { stripEmptyFields: false },
|
||||
schema,
|
||||
});
|
||||
const { submit } = form;
|
||||
const [isEditTags, setIsEditTags] = useState(false);
|
||||
|
||||
const onSubmitTags = useCallback(async () => {
|
||||
const { isValid, data: newData } = await submit();
|
||||
if (isValid && newData.tags) {
|
||||
onSubmit(newData.tags);
|
||||
form.reset({ defaultValue: newData });
|
||||
setIsEditTags(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [onSubmit, submit]);
|
||||
const onSubmitTags = useCallback(async () => {
|
||||
const { isValid, data: newData } = await submit();
|
||||
if (isValid && newData.tags) {
|
||||
onSubmit(newData.tags);
|
||||
form.reset({ defaultValue: newData });
|
||||
setIsEditTags(false);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [onSubmit, submit]);
|
||||
|
||||
const { data: tagOptions = [] } = useGetTags();
|
||||
const [options, setOptions] = useState(
|
||||
tagOptions.map((label) => ({
|
||||
label,
|
||||
}))
|
||||
);
|
||||
const { data: tagOptions = [] } = useGetTags();
|
||||
const [options, setOptions] = useState(
|
||||
tagOptions.map((label) => ({
|
||||
label,
|
||||
}))
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() =>
|
||||
setOptions(
|
||||
tagOptions.map((label) => ({
|
||||
label,
|
||||
}))
|
||||
),
|
||||
[tagOptions]
|
||||
);
|
||||
return (
|
||||
<EuiText data-test-subj="case-view-tag-list">
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="xs"
|
||||
justifyContent="spaceBetween"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<h4>{i18n.TAGS}</h4>
|
||||
useEffect(
|
||||
() =>
|
||||
setOptions(
|
||||
tagOptions.map((label) => ({
|
||||
label,
|
||||
}))
|
||||
),
|
||||
[tagOptions]
|
||||
);
|
||||
return (
|
||||
<EuiText data-test-subj="case-view-tag-list">
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="xs"
|
||||
justifyContent="spaceBetween"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<h4>{i18n.TAGS}</h4>
|
||||
</EuiFlexItem>
|
||||
{isLoading && <EuiLoadingSpinner data-test-subj="tag-list-loading" />}
|
||||
{!isLoading && permissions.all && (
|
||||
<EuiFlexItem data-test-subj="tag-list-edit" grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="tag-list-edit-button"
|
||||
aria-label={i18n.EDIT_TAGS_ARIA}
|
||||
iconType={'pencil'}
|
||||
onClick={setIsEditTags.bind(null, true)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
{isLoading && <EuiLoadingSpinner data-test-subj="tag-list-loading" />}
|
||||
{!isLoading && userCanCrud && (
|
||||
<EuiFlexItem data-test-subj="tag-list-edit" grow={false}>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="tag-list-edit-button"
|
||||
aria-label={i18n.EDIT_TAGS_ARIA}
|
||||
iconType={'pencil'}
|
||||
onClick={setIsEditTags.bind(null, true)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<MyFlexGroup gutterSize="none" data-test-subj="case-tags">
|
||||
{tags.length === 0 && !isEditTags && <p data-test-subj="no-tags">{i18n.NO_TAGS}</p>}
|
||||
{!isEditTags && (
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<MyFlexGroup gutterSize="none" data-test-subj="case-tags">
|
||||
{tags.length === 0 && !isEditTags && <p data-test-subj="no-tags">{i18n.NO_TAGS}</p>}
|
||||
{!isEditTags && (
|
||||
<EuiFlexItem>
|
||||
<Tags tags={tags} color="hollow" />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{isEditTags && (
|
||||
<ColumnFlexGroup data-test-subj="edit-tags" direction="column">
|
||||
<EuiFlexItem>
|
||||
<Tags tags={tags} color="hollow" />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{isEditTags && (
|
||||
<ColumnFlexGroup data-test-subj="edit-tags" direction="column">
|
||||
<EuiFlexItem>
|
||||
<Form form={form}>
|
||||
<CommonUseField
|
||||
path="tags"
|
||||
componentProps={{
|
||||
idAria: 'caseTags',
|
||||
'data-test-subj': 'caseTags',
|
||||
euiFieldProps: {
|
||||
fullWidth: true,
|
||||
placeholder: '',
|
||||
options,
|
||||
noSuggestions: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<FormDataProvider pathsToWatch="tags">
|
||||
{({ tags: anotherTags }) => {
|
||||
const current: string[] = options.map((opt) => opt.label);
|
||||
const newOptions = anotherTags.reduce((acc: string[], item: string) => {
|
||||
if (!acc.includes(item)) {
|
||||
return [...acc, item];
|
||||
}
|
||||
return acc;
|
||||
}, current);
|
||||
if (!isEqual(current, newOptions)) {
|
||||
setOptions(
|
||||
newOptions.map((label: string) => ({
|
||||
label,
|
||||
}))
|
||||
);
|
||||
<Form form={form}>
|
||||
<CommonUseField
|
||||
path="tags"
|
||||
componentProps={{
|
||||
idAria: 'caseTags',
|
||||
'data-test-subj': 'caseTags',
|
||||
euiFieldProps: {
|
||||
fullWidth: true,
|
||||
placeholder: '',
|
||||
options,
|
||||
noSuggestions: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<FormDataProvider pathsToWatch="tags">
|
||||
{({ tags: anotherTags }) => {
|
||||
const current: string[] = options.map((opt) => opt.label);
|
||||
const newOptions = anotherTags.reduce((acc: string[], item: string) => {
|
||||
if (!acc.includes(item)) {
|
||||
return [...acc, item];
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</FormDataProvider>
|
||||
</Form>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="success"
|
||||
data-test-subj="edit-tags-submit"
|
||||
fill
|
||||
iconType="save"
|
||||
onClick={onSubmitTags}
|
||||
size="s"
|
||||
>
|
||||
{i18n.SAVE}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="edit-tags-cancel"
|
||||
iconType="cross"
|
||||
onClick={setIsEditTags.bind(null, false)}
|
||||
size="s"
|
||||
>
|
||||
{i18n.CANCEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</ColumnFlexGroup>
|
||||
)}
|
||||
</MyFlexGroup>
|
||||
</EuiText>
|
||||
);
|
||||
}
|
||||
);
|
||||
return acc;
|
||||
}, current);
|
||||
if (!isEqual(current, newOptions)) {
|
||||
setOptions(
|
||||
newOptions.map((label: string) => ({
|
||||
label,
|
||||
}))
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</FormDataProvider>
|
||||
</Form>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="success"
|
||||
data-test-subj="edit-tags-submit"
|
||||
fill
|
||||
iconType="save"
|
||||
onClick={onSubmitTags}
|
||||
size="s"
|
||||
>
|
||||
{i18n.SAVE}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="edit-tags-cancel"
|
||||
iconType="cross"
|
||||
onClick={setIsEditTags.bind(null, false)}
|
||||
size="s"
|
||||
>
|
||||
{i18n.CANCEL}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</ColumnFlexGroup>
|
||||
)}
|
||||
</MyFlexGroup>
|
||||
</EuiText>
|
||||
);
|
||||
});
|
||||
|
||||
TagList.displayName = 'TagList';
|
||||
|
|
|
@ -11,7 +11,7 @@ import { render, screen } from '@testing-library/react';
|
|||
|
||||
import '../../common/mock/match_media';
|
||||
import { usePushToService, ReturnUsePushToService, UsePushToService } from '.';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { readCasesPermissions, TestProviders } from '../../common/mock';
|
||||
import { CaseStatuses, ConnectorTypes } from '../../../common/api';
|
||||
import { usePostPushToService } from '../../containers/use_post_push_to_service';
|
||||
import { basicPush, actionLicenses, connectorsMock } from '../../containers/mock';
|
||||
|
@ -70,7 +70,6 @@ describe('usePushToService', () => {
|
|||
hasDataToPush: true,
|
||||
onEditClick,
|
||||
isValidConnector: true,
|
||||
userCanCrud: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -281,8 +280,6 @@ describe('usePushToService', () => {
|
|||
});
|
||||
|
||||
describe('user does not have write permissions', () => {
|
||||
const noWriteProps = { ...defaultArgs, userCanCrud: false };
|
||||
|
||||
it('does not display a message when user does not have a premium license', async () => {
|
||||
useFetchActionLicenseMock.mockImplementation(() => ({
|
||||
isLoading: false,
|
||||
|
@ -293,9 +290,11 @@ describe('usePushToService', () => {
|
|||
}));
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() => usePushToService(noWriteProps),
|
||||
() => usePushToService(defaultArgs),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -313,9 +312,11 @@ describe('usePushToService', () => {
|
|||
}));
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() => usePushToService(noWriteProps),
|
||||
() => usePushToService(defaultArgs),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -328,7 +329,7 @@ describe('usePushToService', () => {
|
|||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() =>
|
||||
usePushToService({
|
||||
...noWriteProps,
|
||||
...defaultArgs,
|
||||
connectors: [],
|
||||
connector: {
|
||||
id: 'none',
|
||||
|
@ -338,7 +339,9 @@ describe('usePushToService', () => {
|
|||
},
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -351,7 +354,7 @@ describe('usePushToService', () => {
|
|||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() =>
|
||||
usePushToService({
|
||||
...noWriteProps,
|
||||
...defaultArgs,
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
|
@ -360,7 +363,9 @@ describe('usePushToService', () => {
|
|||
},
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -373,7 +378,7 @@ describe('usePushToService', () => {
|
|||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() =>
|
||||
usePushToService({
|
||||
...noWriteProps,
|
||||
...defaultArgs,
|
||||
connector: {
|
||||
id: 'not-exist',
|
||||
name: 'not-exist',
|
||||
|
@ -383,7 +388,9 @@ describe('usePushToService', () => {
|
|||
isValidConnector: false,
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -396,7 +403,7 @@ describe('usePushToService', () => {
|
|||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() =>
|
||||
usePushToService({
|
||||
...noWriteProps,
|
||||
...defaultArgs,
|
||||
connectors: [],
|
||||
connector: {
|
||||
id: 'not-exist',
|
||||
|
@ -407,7 +414,9 @@ describe('usePushToService', () => {
|
|||
isValidConnector: false,
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
@ -420,11 +429,13 @@ describe('usePushToService', () => {
|
|||
const { result, waitForNextUpdate } = renderHook<UsePushToService, ReturnUsePushToService>(
|
||||
() =>
|
||||
usePushToService({
|
||||
...noWriteProps,
|
||||
...defaultArgs,
|
||||
caseStatus: CaseStatuses.closed,
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders> {children}</TestProviders>,
|
||||
wrapper: ({ children }) => (
|
||||
<TestProviders permissions={readCasesPermissions()}> {children}</TestProviders>
|
||||
),
|
||||
}
|
||||
);
|
||||
await waitForNextUpdate();
|
||||
|
|
|
@ -23,6 +23,7 @@ import { CaseServices } from '../../containers/use_get_case_user_actions';
|
|||
import { ErrorMessage } from './callout/types';
|
||||
import { useRefreshCaseViewPage } from '../case_view/use_on_refresh_case_view_page';
|
||||
import { useGetActionLicense } from '../../containers/use_get_action_license';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
export interface UsePushToService {
|
||||
caseId: string;
|
||||
|
@ -33,7 +34,6 @@ export interface UsePushToService {
|
|||
hasDataToPush: boolean;
|
||||
isValidConnector: boolean;
|
||||
onEditClick: () => void;
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
export interface ReturnUsePushToService {
|
||||
|
@ -50,8 +50,8 @@ export const usePushToService = ({
|
|||
hasDataToPush,
|
||||
isValidConnector,
|
||||
onEditClick,
|
||||
userCanCrud,
|
||||
}: UsePushToService): ReturnUsePushToService => {
|
||||
const { permissions } = useCasesContext();
|
||||
const { isLoading, pushCaseToExternalService } = usePostPushToService();
|
||||
|
||||
const { isLoading: loadingLicense, data: actionLicense = null } = useGetActionLicense();
|
||||
|
@ -76,7 +76,7 @@ export const usePushToService = ({
|
|||
|
||||
// these message require that the user do some sort of write action as a result of the message, readonly users won't
|
||||
// be able to perform such an action so let's not display the error to the user in that situation
|
||||
if (!userCanCrud) {
|
||||
if (!permissions.all) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ export const usePushToService = ({
|
|||
|
||||
return errors;
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [actionLicense, caseStatus, connectors.length, connector, loadingLicense, userCanCrud]);
|
||||
}, [actionLicense, caseStatus, connectors.length, connector, loadingLicense, permissions.all]);
|
||||
|
||||
const pushToServiceButton = useMemo(
|
||||
() => (
|
||||
|
@ -126,7 +126,7 @@ export const usePushToService = ({
|
|||
isLoading ||
|
||||
loadingLicense ||
|
||||
errorsMsg.length > 0 ||
|
||||
!userCanCrud ||
|
||||
!permissions.all ||
|
||||
!isValidConnector ||
|
||||
!hasDataToPush
|
||||
}
|
||||
|
@ -146,29 +146,26 @@ export const usePushToService = ({
|
|||
hasDataToPush,
|
||||
isLoading,
|
||||
loadingLicense,
|
||||
userCanCrud,
|
||||
permissions.all,
|
||||
isValidConnector,
|
||||
]
|
||||
);
|
||||
|
||||
const objToReturn = useMemo(
|
||||
() => ({
|
||||
pushButton:
|
||||
errorsMsg.length > 0 || !hasDataToPush ? (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
title={
|
||||
errorsMsg.length > 0 ? errorsMsg[0].title : i18n.PUSH_LOCKED_TITLE(connector.name)
|
||||
}
|
||||
content={
|
||||
<p>{errorsMsg.length > 0 ? errorsMsg[0].description : i18n.PUSH_LOCKED_DESC}</p>
|
||||
}
|
||||
>
|
||||
{pushToServiceButton}
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
<>{pushToServiceButton}</>
|
||||
),
|
||||
const objToReturn = useMemo(() => {
|
||||
const hidePushButton = errorsMsg.length > 0 || !hasDataToPush || !permissions.all;
|
||||
|
||||
return {
|
||||
pushButton: hidePushButton ? (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
title={errorsMsg.length > 0 ? errorsMsg[0].title : i18n.PUSH_LOCKED_TITLE(connector.name)}
|
||||
content={<p>{errorsMsg.length > 0 ? errorsMsg[0].description : i18n.PUSH_LOCKED_DESC}</p>}
|
||||
>
|
||||
{pushToServiceButton}
|
||||
</EuiToolTip>
|
||||
) : (
|
||||
<>{pushToServiceButton}</>
|
||||
),
|
||||
pushCallouts:
|
||||
errorsMsg.length > 0 ? (
|
||||
<CaseCallOut
|
||||
|
@ -178,17 +175,17 @@ export const usePushToService = ({
|
|||
onEditClick={onEditClick}
|
||||
/>
|
||||
) : null,
|
||||
}),
|
||||
[
|
||||
connector.name,
|
||||
connectors.length,
|
||||
errorsMsg,
|
||||
hasDataToPush,
|
||||
hasLicenseError,
|
||||
onEditClick,
|
||||
pushToServiceButton,
|
||||
]
|
||||
);
|
||||
};
|
||||
}, [
|
||||
connector.name,
|
||||
connectors.length,
|
||||
errorsMsg,
|
||||
hasDataToPush,
|
||||
hasLicenseError,
|
||||
onEditClick,
|
||||
pushToServiceButton,
|
||||
permissions.all,
|
||||
]);
|
||||
|
||||
return objToReturn;
|
||||
};
|
||||
|
|
|
@ -42,7 +42,6 @@ const getCreateCommentUserAction = ({
|
|||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
comment,
|
||||
userCanCrud,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -68,7 +67,6 @@ const getCreateCommentUserAction = ({
|
|||
case CommentType.user:
|
||||
const userBuilder = createUserAttachmentUserActionBuilder({
|
||||
comment,
|
||||
userCanCrud,
|
||||
outlined: comment.id === selectedOutlineCommentId,
|
||||
isEdit: manageMarkdownEditIds.includes(comment.id),
|
||||
commentRefs,
|
||||
|
@ -116,7 +114,6 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
caseData,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
userAction,
|
||||
userCanCrud,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -152,7 +149,6 @@ export const createCommentUserActionBuilder: UserActionBuilder = ({
|
|||
userAction: commentUserAction,
|
||||
externalReferenceAttachmentTypeRegistry,
|
||||
comment,
|
||||
userCanCrud,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
|
|
@ -20,7 +20,6 @@ import { UserActionBuilderArgs, UserActionBuilder } from '../types';
|
|||
|
||||
type BuilderArgs = Pick<
|
||||
UserActionBuilderArgs,
|
||||
| 'userCanCrud'
|
||||
| 'handleManageMarkdownEditId'
|
||||
| 'handleSaveComment'
|
||||
| 'handleManageQuote'
|
||||
|
@ -35,7 +34,6 @@ type BuilderArgs = Pick<
|
|||
|
||||
export const createUserAttachmentUserActionBuilder = ({
|
||||
comment,
|
||||
userCanCrud,
|
||||
outlined,
|
||||
isEdit,
|
||||
isLoading,
|
||||
|
@ -95,7 +93,6 @@ export const createUserAttachmentUserActionBuilder = ({
|
|||
onEdit={handleManageMarkdownEditId.bind(null, comment.id)}
|
||||
onQuote={handleManageQuote.bind(null, comment.comment)}
|
||||
onDelete={handleDeleteComment.bind(null, comment.id)}
|
||||
userCanCrud={userCanCrud}
|
||||
/>
|
||||
),
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React from 'react';
|
||||
import { mount, ReactWrapper } from 'enzyme';
|
||||
import { UserActionContentToolbar, UserActionContentToolbarProps } from './content_toolbar';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
|
||||
jest.mock('../../common/navigation/hooks');
|
||||
jest.mock('../../common/lib/kibana');
|
||||
|
@ -17,7 +18,6 @@ const props: UserActionContentToolbarProps = {
|
|||
id: '1',
|
||||
editLabel: 'edit',
|
||||
quoteLabel: 'quote',
|
||||
userCanCrud: true,
|
||||
isLoading: false,
|
||||
onEdit: jest.fn(),
|
||||
onQuote: jest.fn(),
|
||||
|
@ -27,7 +27,11 @@ describe('UserActionContentToolbar ', () => {
|
|||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<UserActionContentToolbar {...props} />);
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<UserActionContentToolbar {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
||||
it('it renders', async () => {
|
||||
|
|
|
@ -22,7 +22,6 @@ export interface UserActionContentToolbarProps {
|
|||
onEdit: (id: string) => void;
|
||||
onQuote: (id: string) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
const UserActionContentToolbarComponent = ({
|
||||
|
@ -36,7 +35,6 @@ const UserActionContentToolbarComponent = ({
|
|||
onEdit,
|
||||
onQuote,
|
||||
onDelete,
|
||||
userCanCrud,
|
||||
}: UserActionContentToolbarProps) => (
|
||||
<EuiFlexGroup responsive={false} alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -53,7 +51,6 @@ const UserActionContentToolbarComponent = ({
|
|||
onEdit={onEdit}
|
||||
onQuote={onQuote}
|
||||
onDelete={onDelete}
|
||||
userCanCrud={userCanCrud}
|
||||
commentMarkdown={commentMarkdown}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -27,7 +27,6 @@ type GetDescriptionUserActionArgs = Pick<
|
|||
| 'caseData'
|
||||
| 'commentRefs'
|
||||
| 'manageMarkdownEditIds'
|
||||
| 'userCanCrud'
|
||||
| 'handleManageMarkdownEditId'
|
||||
| 'handleManageQuote'
|
||||
> &
|
||||
|
@ -38,7 +37,6 @@ export const getDescriptionUserAction = ({
|
|||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
isLoadingDescription,
|
||||
userCanCrud,
|
||||
onUpdateField,
|
||||
handleManageMarkdownEditId,
|
||||
handleManageQuote,
|
||||
|
@ -85,7 +83,6 @@ export const getDescriptionUserAction = ({
|
|||
isLoading={isLoadingDescription}
|
||||
onEdit={handleManageMarkdownEditId.bind(null, DESCRIPTION_ID)}
|
||||
onQuote={handleManageQuote.bind(null, caseData.description)}
|
||||
userCanCrud={userCanCrud}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
|
|
@ -44,7 +44,6 @@ const defaultProps = {
|
|||
selectedAlertPatterns: ['some-test-pattern'],
|
||||
statusActionButton: null,
|
||||
updateCase,
|
||||
userCanCrud: true,
|
||||
useFetchAlertData: (): [boolean, Record<string, unknown>] => [
|
||||
false,
|
||||
{ 'some-id': { _id: 'some-id' } },
|
||||
|
|
|
@ -91,7 +91,6 @@ export const UserActions = React.memo(
|
|||
onUpdateField,
|
||||
statusActionButton,
|
||||
useFetchAlertData,
|
||||
userCanCrud,
|
||||
}: UserActionTreeProps) => {
|
||||
const { detailName: caseId, commentId } = useCaseViewParams();
|
||||
const [initLoading, setInitLoading] = useState(true);
|
||||
|
@ -123,7 +122,6 @@ export const UserActions = React.memo(
|
|||
<AddComment
|
||||
id={NEW_COMMENT_ID}
|
||||
caseId={caseId}
|
||||
userCanCrud={userCanCrud}
|
||||
ref={(element) => (commentRefs.current[NEW_COMMENT_ID] = element)}
|
||||
onCommentPosted={handleUpdate}
|
||||
onCommentSaving={handleManageMarkdownEditId.bind(null, NEW_COMMENT_ID)}
|
||||
|
@ -131,14 +129,7 @@ export const UserActions = React.memo(
|
|||
statusActionButton={statusActionButton}
|
||||
/>
|
||||
),
|
||||
[
|
||||
caseId,
|
||||
userCanCrud,
|
||||
handleUpdate,
|
||||
handleManageMarkdownEditId,
|
||||
statusActionButton,
|
||||
commentRefs,
|
||||
]
|
||||
[caseId, handleUpdate, handleManageMarkdownEditId, statusActionButton, commentRefs]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -157,7 +148,6 @@ export const UserActions = React.memo(
|
|||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
isLoadingDescription,
|
||||
userCanCrud,
|
||||
onUpdateField,
|
||||
handleManageMarkdownEditId,
|
||||
handleManageQuote,
|
||||
|
@ -167,7 +157,6 @@ export const UserActions = React.memo(
|
|||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
isLoadingDescription,
|
||||
userCanCrud,
|
||||
onUpdateField,
|
||||
handleManageMarkdownEditId,
|
||||
handleManageQuote,
|
||||
|
@ -195,7 +184,6 @@ export const UserActions = React.memo(
|
|||
caseServices,
|
||||
comments: caseData.comments,
|
||||
index,
|
||||
userCanCrud,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -222,7 +210,6 @@ export const UserActions = React.memo(
|
|||
descriptionCommentListObj,
|
||||
caseData,
|
||||
caseServices,
|
||||
userCanCrud,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds,
|
||||
selectedOutlineCommentId,
|
||||
|
@ -241,7 +228,9 @@ export const UserActions = React.memo(
|
|||
]
|
||||
);
|
||||
|
||||
const bottomActions = userCanCrud
|
||||
const { permissions } = useCasesContext();
|
||||
|
||||
const bottomActions = permissions.all
|
||||
? [
|
||||
{
|
||||
username: (
|
||||
|
|
|
@ -67,7 +67,6 @@ export const getMockBuilderArgs = (): UserActionBuilderArgs => {
|
|||
caseServices,
|
||||
index: 0,
|
||||
alertData,
|
||||
userCanCrud: true,
|
||||
commentRefs,
|
||||
manageMarkdownEditIds: [],
|
||||
selectedOutlineCommentId: '',
|
||||
|
|
|
@ -10,6 +10,7 @@ import { mount, ReactWrapper } from 'enzyme';
|
|||
import { UserActionPropertyActions } from './property_actions';
|
||||
import { render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
|
||||
jest.mock('../../common/lib/kibana');
|
||||
|
||||
|
@ -24,14 +25,17 @@ const props = {
|
|||
isLoading: false,
|
||||
onEdit,
|
||||
onQuote,
|
||||
userCanCrud: true,
|
||||
};
|
||||
|
||||
describe('UserActionPropertyActions ', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<UserActionPropertyActions {...props} />);
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -65,7 +69,11 @@ describe('UserActionPropertyActions ', () => {
|
|||
});
|
||||
|
||||
it('shows the spinner when loading', async () => {
|
||||
wrapper = mount(<UserActionPropertyActions {...props} isLoading={true} />);
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...props} isLoading={true} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="user-action-title-loading"]').first().exists()
|
||||
).toBeTruthy();
|
||||
|
@ -82,14 +90,22 @@ describe('UserActionPropertyActions ', () => {
|
|||
deleteConfirmlabel: 'confirm delete me',
|
||||
};
|
||||
it('shows the delete button', () => {
|
||||
const renderResult = render(<UserActionPropertyActions {...deleteProps} />);
|
||||
const renderResult = render(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...deleteProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
userEvent.click(renderResult.getByTestId('property-actions-ellipses'));
|
||||
expect(renderResult.getByTestId('property-actions-trash')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows a confirm dialog when the delete button is clicked', () => {
|
||||
const renderResult = render(<UserActionPropertyActions {...deleteProps} />);
|
||||
const renderResult = render(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...deleteProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
userEvent.click(renderResult.getByTestId('property-actions-ellipses'));
|
||||
userEvent.click(renderResult.getByTestId('property-actions-trash'));
|
||||
|
@ -98,7 +114,11 @@ describe('UserActionPropertyActions ', () => {
|
|||
});
|
||||
|
||||
it('closes the confirm dialog when the cancel button is clicked', () => {
|
||||
const renderResult = render(<UserActionPropertyActions {...deleteProps} />);
|
||||
const renderResult = render(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...deleteProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
userEvent.click(renderResult.getByTestId('property-actions-ellipses'));
|
||||
userEvent.click(renderResult.getByTestId('property-actions-trash'));
|
||||
|
@ -109,7 +129,11 @@ describe('UserActionPropertyActions ', () => {
|
|||
});
|
||||
|
||||
it('calls onDelete when the confirm is pressed', () => {
|
||||
const renderResult = render(<UserActionPropertyActions {...deleteProps} />);
|
||||
const renderResult = render(
|
||||
<TestProviders>
|
||||
<UserActionPropertyActions {...deleteProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
userEvent.click(renderResult.getByTestId('property-actions-ellipses'));
|
||||
userEvent.click(renderResult.getByTestId('property-actions-trash'));
|
||||
|
|
|
@ -11,6 +11,7 @@ import { EuiConfirmModal, EuiLoadingSpinner } from '@elastic/eui';
|
|||
import { PropertyActions } from '../property_actions';
|
||||
import { useLensOpenVisualization } from '../markdown_editor/plugins/lens/use_lens_open_visualization';
|
||||
import { CANCEL_BUTTON, CONFIRM_BUTTON } from './translations';
|
||||
import { useCasesContext } from '../cases_context/use_cases_context';
|
||||
|
||||
interface UserActionPropertyActionsProps {
|
||||
id: string;
|
||||
|
@ -22,7 +23,6 @@ interface UserActionPropertyActionsProps {
|
|||
onEdit: (id: string) => void;
|
||||
onDelete?: (id: string) => void;
|
||||
onQuote: (id: string) => void;
|
||||
userCanCrud: boolean;
|
||||
commentMarkdown: string;
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,9 @@ const UserActionPropertyActionsComponent = ({
|
|||
onEdit,
|
||||
onDelete,
|
||||
onQuote,
|
||||
userCanCrud,
|
||||
commentMarkdown,
|
||||
}: UserActionPropertyActionsProps) => {
|
||||
const { permissions } = useCasesContext();
|
||||
const { canUseEditor, actionConfig } = useLensOpenVisualization({ comment: commentMarkdown });
|
||||
const onEditClick = useCallback(() => onEdit(id), [id, onEdit]);
|
||||
const onQuoteClick = useCallback(() => onQuote(id), [id, onQuote]);
|
||||
|
@ -62,7 +62,7 @@ const UserActionPropertyActionsComponent = ({
|
|||
const propertyActions = useMemo(
|
||||
() =>
|
||||
[
|
||||
userCanCrud
|
||||
permissions.all
|
||||
? [
|
||||
{
|
||||
iconType: 'pencil',
|
||||
|
@ -88,7 +88,7 @@ const UserActionPropertyActionsComponent = ({
|
|||
canUseEditor && actionConfig ? [actionConfig] : [],
|
||||
].flat(),
|
||||
[
|
||||
userCanCrud,
|
||||
permissions.all,
|
||||
editLabel,
|
||||
onEditClick,
|
||||
deleteLabel,
|
||||
|
|
|
@ -30,7 +30,6 @@ export interface UserActionTreeProps {
|
|||
onUpdateField: ({ key, value, onSuccess, onError }: OnUpdateFields) => void;
|
||||
statusActionButton: JSX.Element | null;
|
||||
useFetchAlertData: UseFetchAlertData;
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
type UnsupportedUserActionTypes = typeof UNSUPPORTED_ACTION_TYPES[number];
|
||||
|
@ -43,7 +42,6 @@ export interface UserActionBuilderArgs {
|
|||
caseServices: CaseServices;
|
||||
comments: Comment[];
|
||||
index: number;
|
||||
userCanCrud: boolean;
|
||||
commentRefs: React.MutableRefObject<
|
||||
Record<string, AddCommentRefObject | UserActionMarkdownRefObject | null | undefined>
|
||||
>;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { fireEvent } from '@testing-library/dom';
|
|||
import { AddToCaseAction } from './add_to_case_action';
|
||||
import * as useCaseHook from '../hooks/use_add_to_case';
|
||||
import * as datePicker from '../components/date_range_picker';
|
||||
import * as useGetUserCasesPermissionsModule from '../../../../hooks/use_get_user_cases_permissions';
|
||||
import moment from 'moment';
|
||||
|
||||
describe('AddToCaseAction', function () {
|
||||
|
@ -81,6 +82,10 @@ describe('AddToCaseAction', function () {
|
|||
});
|
||||
|
||||
it('should be able to click add to case button', async function () {
|
||||
const mockUseGetUserCasesPermissions = jest
|
||||
.spyOn(useGetUserCasesPermissionsModule, 'useGetUserCasesPermissions')
|
||||
.mockImplementation(() => ({ crud: false, read: false }));
|
||||
|
||||
const initSeries = {
|
||||
data: [
|
||||
{
|
||||
|
@ -106,8 +111,13 @@ describe('AddToCaseAction', function () {
|
|||
expect(core?.cases?.ui.getAllCasesSelectorModal).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
owner: ['observability'],
|
||||
userCanCrud: true,
|
||||
permissions: {
|
||||
all: false,
|
||||
read: false,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
mockUseGetUserCasesPermissions.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
GetAllCasesSelectorModalProps,
|
||||
} from '@kbn/cases-plugin/public';
|
||||
import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
import { useGetUserCasesPermissions } from '../../../../hooks/use_get_user_cases_permissions';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
import { useAddToCase } from '../hooks/use_add_to_case';
|
||||
import { observabilityFeatureId, observabilityAppId } from '../../../../../common';
|
||||
|
@ -38,6 +39,8 @@ export function AddToCaseAction({
|
|||
timeRange,
|
||||
}: AddToCaseProps) {
|
||||
const kServices = useKibana<ObservabilityAppServices>().services;
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
|
||||
const {
|
||||
cases,
|
||||
|
@ -74,8 +77,8 @@ export function AddToCaseAction({
|
|||
});
|
||||
|
||||
const getAllCasesSelectorModalProps: GetAllCasesSelectorModalProps = {
|
||||
permissions: casesPermissions,
|
||||
onRowClick: onCaseClicked,
|
||||
userCanCrud: true,
|
||||
owner: [owner],
|
||||
onClose: () => {
|
||||
setIsCasesOpen(false);
|
||||
|
|
|
@ -15,7 +15,10 @@ export interface UseGetUserCasesPermissions {
|
|||
}
|
||||
|
||||
export function useGetUserCasesPermissions() {
|
||||
const [casesPermissions, setCasesPermissions] = useState<UseGetUserCasesPermissions | null>(null);
|
||||
const [casesPermissions, setCasesPermissions] = useState<UseGetUserCasesPermissions>({
|
||||
crud: false,
|
||||
read: false,
|
||||
});
|
||||
const uiCapabilities = useKibana().services.application.capabilities;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -220,6 +220,7 @@ function AlertsPage() {
|
|||
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
|
||||
if (!hasAnyData && !isAllRequestsComplete) {
|
||||
return <LoadingObservability />;
|
||||
|
@ -265,7 +266,7 @@ function AlertsPage() {
|
|||
<EuiFlexItem>
|
||||
<CasesContext
|
||||
owner={[observabilityFeatureId]}
|
||||
userCanCrud={userPermissions?.crud ?? false}
|
||||
permissions={casesPermissions}
|
||||
features={{ alerts: { sync: false } }}
|
||||
>
|
||||
<AlertsTableTGrid
|
||||
|
|
|
@ -201,7 +201,7 @@ function ObservabilityActions({
|
|||
|
||||
const actionsMenuItems = useMemo(() => {
|
||||
return [
|
||||
...(casePermissions?.crud
|
||||
...(casePermissions.crud
|
||||
? [
|
||||
<EuiContextMenuItem
|
||||
data-test-subj="add-to-existing-case-action"
|
||||
|
@ -246,7 +246,7 @@ function ObservabilityActions({
|
|||
],
|
||||
];
|
||||
}, [
|
||||
casePermissions?.crud,
|
||||
casePermissions.crud,
|
||||
handleAddToExistingCaseClick,
|
||||
handleAddToNewCaseClick,
|
||||
linkToRule,
|
||||
|
|
|
@ -13,17 +13,19 @@ import { usePluginContext } from '../../hooks/use_plugin_context';
|
|||
import { LazyAlertsFlyout } from '../..';
|
||||
import { useFetchAlertDetail } from './use_fetch_alert_detail';
|
||||
import { useFetchAlertData } from './use_fetch_alert_data';
|
||||
import { UseGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions';
|
||||
|
||||
interface CasesProps {
|
||||
userCanCrud: boolean;
|
||||
permissions: UseGetUserCasesPermissions;
|
||||
}
|
||||
export const Cases = React.memo<CasesProps>(({ userCanCrud }) => {
|
||||
export const Cases = React.memo<CasesProps>(({ permissions }) => {
|
||||
const {
|
||||
cases,
|
||||
application: { getUrlForApp, navigateToApp },
|
||||
} = useKibana().services;
|
||||
const { observabilityRuleTypeRegistry } = usePluginContext();
|
||||
const [selectedAlertId, setSelectedAlertId] = useState<string>('');
|
||||
const casesPermissions = { all: permissions.crud, read: permissions.read };
|
||||
|
||||
const handleFlyoutClose = useCallback(() => {
|
||||
setSelectedAlertId('');
|
||||
|
@ -44,7 +46,7 @@ export const Cases = React.memo<CasesProps>(({ userCanCrud }) => {
|
|||
)}
|
||||
{cases.ui.getCases({
|
||||
basePath: CASES_PATH,
|
||||
userCanCrud,
|
||||
permissions: casesPermissions,
|
||||
owner: [CASES_OWNER],
|
||||
features: { alerts: { sync: false } },
|
||||
useFetchAlertData,
|
||||
|
|
|
@ -38,13 +38,13 @@ export const CasesPage = React.memo(() => {
|
|||
docsLink: docLinks.links.observability.guide,
|
||||
});
|
||||
|
||||
return userPermissions == null || userPermissions?.read ? (
|
||||
return userPermissions.read ? (
|
||||
<ObservabilityPageTemplate
|
||||
isPageDataLoaded={Boolean(hasAnyData || isAllRequestsComplete)}
|
||||
data-test-subj={noDataConfig ? 'noDataPage' : undefined}
|
||||
noDataConfig={noDataConfig}
|
||||
>
|
||||
<Cases userCanCrud={userPermissions?.crud ?? false} />
|
||||
<Cases permissions={userPermissions} />
|
||||
</ObservabilityPageTemplate>
|
||||
) : (
|
||||
<CaseFeatureNoPermissions />
|
||||
|
|
|
@ -128,6 +128,7 @@ export function OverviewPage({ routeParams }: Props) {
|
|||
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAnyData !== true) {
|
||||
|
@ -199,7 +200,7 @@ export function OverviewPage({ routeParams }: Props) {
|
|||
>
|
||||
<CasesContext
|
||||
owner={[observabilityFeatureId]}
|
||||
userCanCrud={userPermissions?.crud ?? false}
|
||||
permissions={casesPermissions}
|
||||
features={{ alerts: { sync: false } }}
|
||||
>
|
||||
<AlertsTableTGrid
|
||||
|
|
|
@ -56,7 +56,8 @@ const StartAppComponent: FC<StartAppComponent> = ({
|
|||
cases,
|
||||
} = useKibana().services;
|
||||
const [darkMode] = useUiSetting$<boolean>(DEFAULT_DARK_MODE);
|
||||
const casesPermissions = useGetUserCasesPermissions();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
return (
|
||||
<EuiErrorBoundary>
|
||||
|
@ -69,10 +70,7 @@ const StartAppComponent: FC<StartAppComponent> = ({
|
|||
<UserPrivilegesProvider kibanaCapabilities={capabilities}>
|
||||
<ManageUserInfo>
|
||||
<ReactQueryClientProvider>
|
||||
<CasesContext
|
||||
owner={[APP_ID]}
|
||||
userCanCrud={casesPermissions?.crud ?? false}
|
||||
>
|
||||
<CasesContext owner={[APP_ID]} permissions={casesPermissions}>
|
||||
<PageRouter
|
||||
history={history}
|
||||
onAppLeave={onAppLeave}
|
||||
|
|
|
@ -46,6 +46,7 @@ const CaseContainerComponent: React.FC = () => {
|
|||
const { cases } = useKibana().services;
|
||||
const { getAppUrl, navigateTo } = useNavigation();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
const dispatch = useDispatch();
|
||||
const { formatUrl: detectionsFormatUrl, search: detectionsUrlSearch } = useFormatUrl(
|
||||
SecurityPageName.rules
|
||||
|
@ -147,7 +148,7 @@ const CaseContainerComponent: React.FC = () => {
|
|||
},
|
||||
},
|
||||
useFetchAlertData,
|
||||
userCanCrud: userPermissions?.crud ?? false,
|
||||
permissions: casesPermissions,
|
||||
})}
|
||||
</CaseDetailsRefreshContext.Provider>
|
||||
<SpyRoute pageName={SecurityPageName.case} />
|
||||
|
|
|
@ -20,8 +20,16 @@ import { mockAlertDetailsData } from './__mocks__';
|
|||
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { useInvestigationTimeEnrichment } from '../../containers/cti/event_enrichment';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
const originalKibanaLib = jest.requireActual('../../lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock('../../containers/cti/event_enrichment');
|
||||
|
||||
jest.mock('../../../detections/containers/detection_engine/rules/use_rule_with_fallback', () => {
|
||||
|
|
|
@ -29,7 +29,7 @@ export const RelatedCases: React.FC<Props> = React.memo(({ eventId, isReadOnly }
|
|||
const [relatedCases, setRelatedCases] = useState<RelatedCaseList>([]);
|
||||
const [areCasesLoading, setAreCasesLoading] = useState(true);
|
||||
const [hasError, setHasError] = useState<boolean>(false);
|
||||
const hasCasesReadPermissions = casePermissions?.read ?? false;
|
||||
const hasCasesReadPermissions = casePermissions.read;
|
||||
|
||||
const getRelatedCases = useCallback(async () => {
|
||||
let relatedCaseList: RelatedCaseList = [];
|
||||
|
|
|
@ -24,9 +24,17 @@ import { getDefaultControlColumn } from '../../../timelines/components/timeline/
|
|||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
import { defaultCellActions } from '../../lib/cell_actions/default_cell_actions';
|
||||
import { UseFieldBrowserOptionsProps } from '../../../timelines/components/fields_browser';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
const originalKibanaLib = jest.requireActual('../../lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock('../../../timelines/containers', () => ({
|
||||
useTimelineEvents: jest.fn(),
|
||||
}));
|
||||
|
|
|
@ -131,6 +131,16 @@ Object {
|
|||
"name": "Timelines",
|
||||
"onClick": [Function],
|
||||
},
|
||||
Object {
|
||||
"data-href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"data-test-subj": "navigation-cases",
|
||||
"disabled": false,
|
||||
"href": "securitySolutionUI/cases?query=(language:kuery,query:'host.name:%22security-solution-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)),timeline:(linkTo:!(global),timerange:(from:'2020-07-07T08:20:18.966Z',fromStr:now-24h,kind:relative,to:'2020-07-08T08:20:18.966Z',toStr:now)))",
|
||||
"id": "cases",
|
||||
"isSelected": false,
|
||||
"name": "Cases",
|
||||
"onClick": [Function],
|
||||
},
|
||||
],
|
||||
"name": "Investigate",
|
||||
},
|
||||
|
|
|
@ -23,6 +23,13 @@ import { useCanSeeHostIsolationExceptionsMenu } from '../../../../management/pag
|
|||
|
||||
jest.mock('../../../lib/kibana/kibana_react');
|
||||
jest.mock('../../../lib/kibana');
|
||||
const originalKibanaLib = jest.requireActual('../../../lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock('../../../hooks/use_selector');
|
||||
jest.mock('../../../hooks/use_experimental_features');
|
||||
jest.mock('../../../utils/route/use_route_spy');
|
||||
|
@ -132,7 +139,7 @@ describe('useSecuritySolutionNavigation', () => {
|
|||
});
|
||||
|
||||
it('should omit host isolation exceptions if hook reports false', () => {
|
||||
(useCanSeeHostIsolationExceptionsMenu as jest.Mock).mockReturnValueOnce(false);
|
||||
(useCanSeeHostIsolationExceptionsMenu as jest.Mock).mockReturnValue(false);
|
||||
const { result } = renderHook<{}, KibanaPageTemplateProps['solutionNav']>(
|
||||
() => useSecuritySolutionNavigation(),
|
||||
{ wrapper: TestProviders }
|
||||
|
|
|
@ -68,7 +68,7 @@ export const usePrimaryNavigationItems = ({
|
|||
};
|
||||
|
||||
function usePrimaryNavigationItemsToDisplay(navTabs: Record<string, NavTab>) {
|
||||
const hasCasesReadPermissions = useGetUserCasesPermissions()?.read;
|
||||
const hasCasesReadPermissions = useGetUserCasesPermissions().read;
|
||||
const canSeeHostIsolationExceptions = useCanSeeHostIsolationExceptionsMenu();
|
||||
const isPolicyListEnabled = useIsExperimentalFeatureEnabled('policyListEnabled');
|
||||
const uiCapabilities = useKibana().services.application.capabilities;
|
||||
|
|
|
@ -12,9 +12,17 @@ import { TEST_ID, SessionsView, defaultSessionsFilter } from '.';
|
|||
import { EntityType, TimelineId } from '@kbn/timelines-plugin/common';
|
||||
import { SessionsComponentsProps } from './types';
|
||||
import { TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
const originalKibanaLib = jest.requireActual('../../lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock('../url_state/normalize_time_range');
|
||||
|
||||
const startDate = '2022-03-22T22:10:56.794Z';
|
||||
|
|
|
@ -14,7 +14,6 @@ import { ModalInspectQuery } from '../inspect/modal';
|
|||
import { useInspect } from '../inspect/use_inspect';
|
||||
import { useLensAttributes } from './use_lens_attributes';
|
||||
import { useAddToExistingCase } from './use_add_to_existing_case';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
import { useAddToNewCase } from './use_add_to_new_case';
|
||||
import { VisualizationActionsProps } from './types';
|
||||
import {
|
||||
|
@ -54,8 +53,6 @@ const VisualizationActionsComponent: React.FC<VisualizationActionsProps> = ({
|
|||
stackByField,
|
||||
}) => {
|
||||
const { lens } = useKibana().services;
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const userCanCrud = userPermissions?.crud ?? false;
|
||||
|
||||
const { canUseEditor, navigateToPrefilledEditor } = lens;
|
||||
const [isPopoverOpen, setPopover] = useState(false);
|
||||
|
@ -82,14 +79,12 @@ const VisualizationActionsComponent: React.FC<VisualizationActionsProps> = ({
|
|||
onAddToCaseClicked: closePopover,
|
||||
lensAttributes: attributes,
|
||||
timeRange: timerange,
|
||||
userCanCrud,
|
||||
});
|
||||
|
||||
const { onAddToNewCaseClicked, disabled: isAddToNewCaseDisabled } = useAddToNewCase({
|
||||
onClick: closePopover,
|
||||
timeRange: timerange,
|
||||
lensAttributes: attributes,
|
||||
userCanCrud,
|
||||
});
|
||||
|
||||
const onOpenInLens = useCallback(() => {
|
||||
|
|
|
@ -5,25 +5,45 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__';
|
||||
import { kpiHostMetricLensAttributes } from './lens_attributes/hosts/kpi_host_metric';
|
||||
import { useAddToExistingCase } from './use_add_to_existing_case';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
|
||||
jest.mock('../../lib/kibana/kibana_react');
|
||||
const mockedUseKibana = mockUseKibana();
|
||||
const mockGetUseCasesAddToExistingCaseModal = jest.fn();
|
||||
|
||||
jest.mock('../../lib/kibana', () => {
|
||||
const original = jest.requireActual('../../lib/kibana');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useGetUserCasesPermissions: jest.fn(),
|
||||
useKibana: () => ({
|
||||
...mockedUseKibana,
|
||||
services: {
|
||||
...mockedUseKibana.services,
|
||||
cases: {
|
||||
hooks: {
|
||||
getUseCasesAddToExistingCaseModal: mockGetUseCasesAddToExistingCaseModal,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('useAddToExistingCase', () => {
|
||||
const mockCases = mockCasesContract();
|
||||
const mockOnAddToCaseClicked = jest.fn();
|
||||
const timeRange = {
|
||||
from: '2022-03-06T16:00:00.000Z',
|
||||
to: '2022-03-07T15:59:59.999Z',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
cases: mockCases,
|
||||
},
|
||||
(useGetUserCasesPermissions as jest.Mock).mockReturnValue({
|
||||
crud: true,
|
||||
read: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -32,47 +52,48 @@ describe('useAddToExistingCase', () => {
|
|||
useAddToExistingCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange,
|
||||
userCanCrud: true,
|
||||
onAddToCaseClicked: mockOnAddToCaseClicked,
|
||||
})
|
||||
);
|
||||
expect(mockCases.hooks.getUseCasesAddToExistingCaseModal).toHaveBeenCalledWith({
|
||||
expect(mockGetUseCasesAddToExistingCaseModal).toHaveBeenCalledWith({
|
||||
onClose: mockOnAddToCaseClicked,
|
||||
toastContent: 'Successfully added visualization to the case',
|
||||
});
|
||||
expect(result.current.disabled).toEqual(false);
|
||||
});
|
||||
|
||||
it("button disalbled if user Can't Crud", () => {
|
||||
it("button disabled if user Can't Crud", () => {
|
||||
(useGetUserCasesPermissions as jest.Mock).mockReturnValue({
|
||||
crud: false,
|
||||
read: true,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAddToExistingCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange,
|
||||
userCanCrud: false,
|
||||
onAddToCaseClicked: mockOnAddToCaseClicked,
|
||||
})
|
||||
);
|
||||
expect(result.current.disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('button disalbled if no lensAttributes', () => {
|
||||
it('button disabled if no lensAttributes', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAddToExistingCase({
|
||||
lensAttributes: null,
|
||||
timeRange,
|
||||
userCanCrud: true,
|
||||
onAddToCaseClicked: mockOnAddToCaseClicked,
|
||||
})
|
||||
);
|
||||
expect(result.current.disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('button disalbled if no timeRange', () => {
|
||||
it('button disabled if no timeRange', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAddToExistingCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange: null,
|
||||
userCanCrud: true,
|
||||
onAddToCaseClicked: mockOnAddToCaseClicked,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ import { useCallback, useMemo } from 'react';
|
|||
import { CommentType } from '@kbn/cases-plugin/common';
|
||||
|
||||
import { APP_ID } from '../../../../common/constants';
|
||||
import { useKibana } from '../../lib/kibana/kibana_react';
|
||||
import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
import { ADD_TO_CASE_SUCCESS } from './translations';
|
||||
|
||||
import { LensAttributes } from './types';
|
||||
|
@ -19,13 +19,12 @@ export const useAddToExistingCase = ({
|
|||
onAddToCaseClicked,
|
||||
lensAttributes,
|
||||
timeRange,
|
||||
userCanCrud,
|
||||
}: {
|
||||
onAddToCaseClicked?: () => void;
|
||||
lensAttributes: LensAttributes | null;
|
||||
timeRange: { from: string; to: string } | null;
|
||||
userCanCrud: boolean;
|
||||
}) => {
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const { cases } = useKibana().services;
|
||||
const attachments = useMemo(() => {
|
||||
return [
|
||||
|
@ -54,6 +53,6 @@ export const useAddToExistingCase = ({
|
|||
|
||||
return {
|
||||
onAddToExistingCaseClicked,
|
||||
disabled: lensAttributes == null || timeRange == null || !userCanCrud,
|
||||
disabled: lensAttributes == null || timeRange == null || !userPermissions.crud,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -5,24 +5,45 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__';
|
||||
import { kpiHostMetricLensAttributes } from './lens_attributes/hosts/kpi_host_metric';
|
||||
import { useAddToNewCase } from './use_add_to_new_case';
|
||||
import { useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
|
||||
jest.mock('../../lib/kibana/kibana_react');
|
||||
|
||||
const mockedUseKibana = mockUseKibana();
|
||||
const mockGetUseCasesAddToNewCaseFlyout = jest.fn();
|
||||
|
||||
jest.mock('../../lib/kibana', () => {
|
||||
const original = jest.requireActual('../../lib/kibana');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useGetUserCasesPermissions: jest.fn(),
|
||||
useKibana: () => ({
|
||||
...mockedUseKibana,
|
||||
services: {
|
||||
...mockedUseKibana.services,
|
||||
cases: {
|
||||
hooks: {
|
||||
getUseCasesAddToNewCaseFlyout: mockGetUseCasesAddToNewCaseFlyout,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('useAddToNewCase', () => {
|
||||
const mockCases = mockCasesContract();
|
||||
const timeRange = {
|
||||
from: '2022-03-06T16:00:00.000Z',
|
||||
to: '2022-03-07T15:59:59.999Z',
|
||||
};
|
||||
beforeEach(() => {
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
cases: mockCases,
|
||||
},
|
||||
(useGetUserCasesPermissions as jest.Mock).mockReturnValue({
|
||||
crud: true,
|
||||
read: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -31,43 +52,44 @@ describe('useAddToNewCase', () => {
|
|||
useAddToNewCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange,
|
||||
userCanCrud: true,
|
||||
})
|
||||
);
|
||||
expect(mockCases.hooks.getUseCasesAddToNewCaseFlyout).toHaveBeenCalledWith({
|
||||
expect(mockGetUseCasesAddToNewCaseFlyout).toHaveBeenCalledWith({
|
||||
toastContent: 'Successfully added visualization to the case',
|
||||
});
|
||||
expect(result.current.disabled).toEqual(false);
|
||||
});
|
||||
|
||||
it("button disalbled if user Can't Crud", () => {
|
||||
it("button disabled if user Can't Crud", () => {
|
||||
(useGetUserCasesPermissions as jest.Mock).mockReturnValue({
|
||||
crud: false,
|
||||
read: true,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useAddToNewCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange,
|
||||
userCanCrud: false,
|
||||
})
|
||||
);
|
||||
expect(result.current.disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('button disalbled if no lensAttributes', () => {
|
||||
it('button disabled if no lensAttributes', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAddToNewCase({
|
||||
lensAttributes: null,
|
||||
timeRange,
|
||||
userCanCrud: true,
|
||||
})
|
||||
);
|
||||
expect(result.current.disabled).toEqual(true);
|
||||
});
|
||||
|
||||
it('button disalbled if no timeRange', () => {
|
||||
it('button disabled if no timeRange', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useAddToNewCase({
|
||||
lensAttributes: kpiHostMetricLensAttributes,
|
||||
timeRange: null,
|
||||
userCanCrud: true,
|
||||
})
|
||||
);
|
||||
expect(result.current.disabled).toEqual(true);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { useCallback, useMemo } from 'react';
|
|||
import { CommentType } from '@kbn/cases-plugin/common';
|
||||
|
||||
import { APP_ID } from '../../../../common/constants';
|
||||
import { useKibana } from '../../lib/kibana/kibana_react';
|
||||
import { useKibana, useGetUserCasesPermissions } from '../../lib/kibana';
|
||||
import { ADD_TO_CASE_SUCCESS } from './translations';
|
||||
|
||||
import { LensAttributes } from './types';
|
||||
|
@ -18,17 +18,12 @@ export interface UseAddToNewCaseProps {
|
|||
onClick?: () => void;
|
||||
timeRange: { from: string; to: string } | null;
|
||||
lensAttributes: LensAttributes | null;
|
||||
userCanCrud: boolean;
|
||||
}
|
||||
|
||||
const owner = APP_ID;
|
||||
|
||||
export const useAddToNewCase = ({
|
||||
onClick,
|
||||
timeRange,
|
||||
lensAttributes,
|
||||
userCanCrud,
|
||||
}: UseAddToNewCaseProps) => {
|
||||
export const useAddToNewCase = ({ onClick, timeRange, lensAttributes }: UseAddToNewCaseProps) => {
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const { cases } = useKibana().services;
|
||||
const attachments = useMemo(() => {
|
||||
return [
|
||||
|
@ -57,6 +52,6 @@ export const useAddToNewCase = ({
|
|||
|
||||
return {
|
||||
onAddToNewCaseClicked,
|
||||
disabled: lensAttributes == null || timeRange == null || !userCanCrud,
|
||||
disabled: lensAttributes == null || timeRange == null || !userPermissions.crud,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -152,7 +152,10 @@ export interface UseGetUserCasesPermissions {
|
|||
}
|
||||
|
||||
export const useGetUserCasesPermissions = () => {
|
||||
const [casesPermissions, setCasesPermissions] = useState<UseGetUserCasesPermissions | null>(null);
|
||||
const [casesPermissions, setCasesPermissions] = useState<UseGetUserCasesPermissions>({
|
||||
crud: false,
|
||||
read: false,
|
||||
});
|
||||
const uiCapabilities = useKibana().services.application.capabilities;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -35,7 +35,7 @@ export const useAddToCaseActions = ({
|
|||
}: UseAddToCaseActions) => {
|
||||
const { cases: casesUi } = useKibana().services;
|
||||
const casePermissions = useGetUserCasesPermissions();
|
||||
const hasWritePermissions = casePermissions?.crud ?? false;
|
||||
const hasWritePermissions = casePermissions.crud;
|
||||
|
||||
const isAlert = useMemo(() => {
|
||||
return ecsData?.event?.kind?.includes('signal');
|
||||
|
|
|
@ -20,7 +20,7 @@ export const useBulkAddToCaseActions = ({ onClose, onSuccess }: UseAddToCaseActi
|
|||
const { cases: casesUi } = useKibana().services;
|
||||
|
||||
const casePermissions = useGetUserCasesPermissions();
|
||||
const hasWritePermissions = casePermissions?.crud ?? false;
|
||||
const hasWritePermissions = casePermissions.crud;
|
||||
|
||||
const createCaseFlyout = casesUi.hooks.getUseCasesAddToNewCaseFlyout({
|
||||
onClose,
|
||||
|
|
|
@ -14,10 +14,11 @@ const MAX_CASES_TO_SHOW = 3;
|
|||
const RecentCasesComponent = () => {
|
||||
const { cases } = useKibana().services;
|
||||
|
||||
const userCanCrud = useGetUserCasesPermissions()?.crud ?? false;
|
||||
const permissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: permissions.crud, read: permissions.read };
|
||||
|
||||
return cases.ui.getRecentCases({
|
||||
userCanCrud,
|
||||
permissions: casesPermissions,
|
||||
maxCasesToShow: MAX_CASES_TO_SHOW,
|
||||
owner: [APP_ID],
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ export const Sidebar = React.memo<{
|
|||
);
|
||||
|
||||
// only render the recently created cases view if the user has at least read permissions
|
||||
const hasCasesReadPermissions = useGetUserCasesPermissions()?.read;
|
||||
const hasCasesReadPermissions = useGetUserCasesPermissions().read;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" responsive={false} gutterSize="l">
|
||||
|
|
|
@ -53,7 +53,7 @@ const DetectionResponseComponent = () => {
|
|||
const { indicesExist, indexPattern, loading: isSourcererLoading } = useSourcererDataView();
|
||||
const { signalIndexName } = useSignalIndex();
|
||||
const { hasKibanaREAD, hasIndexRead } = useAlertsPrivileges();
|
||||
const canReadCases = useGetUserCasesPermissions()?.read;
|
||||
const canReadCases = useGetUserCasesPermissions().read;
|
||||
const canReadAlerts = hasKibanaREAD && hasIndexRead;
|
||||
|
||||
if (!canReadAlerts && !canReadCases) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import React from 'react';
|
|||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import { useKibana, useGetUserCasesPermissions } from '../../../../common/lib/kibana';
|
||||
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
|
||||
import { mockTimelineModel, TestProviders } from '../../../../common/mock';
|
||||
import { AddToCaseButton } from '.';
|
||||
|
@ -35,6 +35,13 @@ jest.mock('react-redux', () => {
|
|||
});
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
const originalKibanaLib = jest.requireActual('../../../../common/lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock('../../../../common/hooks/use_selector');
|
||||
|
||||
const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
|
||||
|
|
|
@ -68,6 +68,7 @@ const AddToCaseButtonComponent: React.FC<Props> = ({ timelineId }) => {
|
|||
);
|
||||
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
const casesPermissions = { all: userPermissions.crud, read: userPermissions.read };
|
||||
|
||||
const handleButtonClick = useCallback(() => {
|
||||
setPopover((currentIsOpen) => !currentIsOpen);
|
||||
|
@ -163,8 +164,8 @@ const AddToCaseButtonComponent: React.FC<Props> = ({ timelineId }) => {
|
|||
{isCaseModalOpen &&
|
||||
cases.ui.getAllCasesSelectorModal({
|
||||
onRowClick,
|
||||
userCanCrud: userPermissions?.crud ?? false,
|
||||
owner: [APP_ID],
|
||||
permissions: casesPermissions,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -13,7 +13,11 @@ import { TimelineId } from '../../../../../../common/types/timeline';
|
|||
import { Ecs } from '../../../../../../common/ecs';
|
||||
import { mockAlertDetailsData } from '../../../../../common/components/event_details/__mocks__';
|
||||
import type { TimelineEventsDetailsItem } from '../../../../../../common/search_strategy';
|
||||
import { KibanaServices, useKibana } from '../../../../../common/lib/kibana';
|
||||
import {
|
||||
KibanaServices,
|
||||
useKibana,
|
||||
useGetUserCasesPermissions,
|
||||
} from '../../../../../common/lib/kibana';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { mockCasesContract } from '@kbn/cases-plugin/public/mocks';
|
||||
|
||||
|
@ -64,7 +68,15 @@ jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
|
|||
jest.mock('../../../../../detections/components/user_info', () => ({
|
||||
useUserData: jest.fn().mockReturnValue([{ canUserCRUD: true, hasIndexWrite: true }]),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana');
|
||||
const originalKibanaLib = jest.requireActual('../../../../../common/lib/kibana');
|
||||
|
||||
// Restore the useGetUserCasesPermissions so the calling functions can receive a valid permissions object
|
||||
// The returned permissions object will indicate that the user does not have permissions by default
|
||||
const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock;
|
||||
mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions);
|
||||
|
||||
jest.mock(
|
||||
'../../../../../detections/containers/detection_engine/alerts/use_alerts_privileges',
|
||||
() => ({
|
||||
|
|
|
@ -33,33 +33,37 @@ jest.mock(
|
|||
})
|
||||
);
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana', () => ({
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
application: {
|
||||
navigateToApp: jest.fn(),
|
||||
getUrlForApp: jest.fn(),
|
||||
capabilities: {
|
||||
siem: { crud_alerts: true, read_alerts: true },
|
||||
jest.mock('../../../../../common/lib/kibana', () => {
|
||||
const originalKibanaLib = jest.requireActual('../../../../../common/lib/kibana');
|
||||
|
||||
return {
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
application: {
|
||||
navigateToApp: jest.fn(),
|
||||
getUrlForApp: jest.fn(),
|
||||
capabilities: {
|
||||
siem: { crud_alerts: true, read_alerts: true },
|
||||
},
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
uiSettings: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
savedObjects: {
|
||||
client: {},
|
||||
},
|
||||
timelines: { ...mockTimelines },
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
uiSettings: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
savedObjects: {
|
||||
client: {},
|
||||
},
|
||||
timelines: { ...mockTimelines },
|
||||
},
|
||||
}),
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: jest.fn(),
|
||||
}));
|
||||
}),
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: originalKibanaLib.useGetUserCasesPermissions,
|
||||
};
|
||||
});
|
||||
|
||||
const defaultProps = {
|
||||
ariaRowindex: 2,
|
||||
|
|
|
@ -39,29 +39,33 @@ jest.mock('../../../../../common/components/user_privileges', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana', () => ({
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: { ...mockTimelines },
|
||||
data: {
|
||||
search: jest.fn(),
|
||||
query: jest.fn(),
|
||||
},
|
||||
application: {
|
||||
capabilities: {
|
||||
siem: { crud_alerts: true, read_alerts: true },
|
||||
jest.mock('../../../../../common/lib/kibana', () => {
|
||||
const originalModule = jest.requireActual('../../../../../common/lib/kibana');
|
||||
|
||||
return {
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
timelines: { ...mockTimelines },
|
||||
data: {
|
||||
search: jest.fn(),
|
||||
query: jest.fn(),
|
||||
},
|
||||
application: {
|
||||
capabilities: {
|
||||
siem: { crud_alerts: true, read_alerts: true },
|
||||
},
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
},
|
||||
cases: mockCasesContract(),
|
||||
},
|
||||
}),
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: jest.fn(),
|
||||
}));
|
||||
}),
|
||||
useToasts: jest.fn().mockReturnValue({
|
||||
addError: jest.fn(),
|
||||
addSuccess: jest.fn(),
|
||||
addWarning: jest.fn(),
|
||||
}),
|
||||
useGetUserCasesPermissions: originalModule.useGetUserCasesPermissions,
|
||||
};
|
||||
});
|
||||
|
||||
describe('EventColumnView', () => {
|
||||
useIsExperimentalFeatureEnabledMock.mockReturnValue(false);
|
||||
|
|
|
@ -34,7 +34,6 @@ import { TimelineTabs } from '../../../../../common/types/timeline';
|
|||
import { defaultRowRenderers } from './renderers';
|
||||
import { createStore, State } from '../../../../common/store';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana/hooks');
|
||||
jest.mock('../../../../common/hooks/use_app_toasts');
|
||||
jest.mock('../../../../common/components/user_privileges', () => {
|
||||
return {
|
||||
|
@ -84,7 +83,6 @@ jest.mock('../../../../common/lib/kibana', () => {
|
|||
},
|
||||
},
|
||||
}),
|
||||
useGetUserSavedObjectPermissions: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue