diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts
index 0d552d3af6eea..d56168f3f3479 100644
--- a/x-pack/plugins/cases/common/ui/types.ts
+++ b/x-pack/plugins/cases/common/ui/types.ts
@@ -128,6 +128,7 @@ export interface FilterOptions {
status: CaseStatusWithAllStatus;
tags: string[];
reporters: User[];
+ owner: string[];
onlyCollectionType?: boolean;
}
diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx
index aeff350e416d2..7950f962a9cc1 100644
--- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx
+++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx
@@ -47,6 +47,9 @@ jest.mock('../../containers/use_get_action_license');
jest.mock('../../containers/configure/use_connectors');
jest.mock('../../common/lib/kibana');
jest.mock('../../common/navigation/hooks');
+jest.mock('../app/use_available_owners', () => ({
+ useAvailableCasesOwners: () => ['securitySolution', 'observability'],
+}));
const useDeleteCasesMock = useDeleteCases as jest.Mock;
const useGetCasesMock = useGetCases as jest.Mock;
@@ -780,6 +783,32 @@ describe('AllCasesListGeneric', () => {
expect(doRefresh).toHaveBeenCalled();
});
+ it('shows Solution column if there are no set owners', async () => {
+ const doRefresh = jest.fn();
+
+ const wrapper = mount(
+
+
+
+ );
+
+ const solutionHeader = wrapper.find({ children: 'Solution' });
+ expect(solutionHeader.exists()).toBeTruthy();
+ });
+
+ it('hides Solution column if there is a set owner', async () => {
+ const doRefresh = jest.fn();
+
+ const wrapper = mount(
+
+
+
+ );
+
+ const solutionHeader = wrapper.find({ children: 'Solution' });
+ expect(solutionHeader.exists()).toBeFalsy();
+ });
+
it('should deselect cases when refreshing', async () => {
useGetCasesMock.mockReturnValue({
...defaultGetCases,
diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx
index 37e4ef8c03d36..bf88c5a7906bf 100644
--- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx
+++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx
@@ -23,6 +23,7 @@ import { SELECTABLE_MESSAGE_COLLECTIONS } from '../../common/translations';
import { useGetCases } from '../../containers/use_get_cases';
import { usePostComment } from '../../containers/use_post_comment';
+import { useAvailableCasesOwners } from '../app/use_available_owners';
import { useCasesColumns } from './columns';
import { getExpandedRowMap } from './expanded_row';
import { CasesTableFilters } from './table_filters';
@@ -66,10 +67,15 @@ export const AllCasesList = React.memo(
updateCase,
doRefresh,
}) => {
- const { userCanCrud } = useCasesContext();
+ const { owner, userCanCrud } = useCasesContext();
+ const hasOwner = !!owner.length;
+ const availableSolutions = useAvailableCasesOwners();
+
const firstAvailableStatus = head(difference(caseStatuses, hiddenStatuses));
- const initialFilterOptions =
- !isEmpty(hiddenStatuses) && firstAvailableStatus ? { status: firstAvailableStatus } : {};
+ const initialFilterOptions = {
+ ...(!isEmpty(hiddenStatuses) && firstAvailableStatus && { status: firstAvailableStatus }),
+ owner: hasOwner ? owner : availableSolutions,
+ };
const {
data,
@@ -181,6 +187,7 @@ export const AllCasesList = React.memo(
alertData,
postComment,
updateCase,
+ showSolutionColumn: !hasOwner && availableSolutions.length > 1,
});
const itemIdToExpandedRowMap = useMemo(
@@ -235,11 +242,13 @@ export const AllCasesList = React.memo(
countOpenCases={data.countOpenCases}
countInProgressCases={data.countInProgressCases}
onFilterChanged={onFilterChangedCallback}
+ availableSolutions={hasOwner ? [] : availableSolutions}
initial={{
search: filterOptions.search,
reporters: filterOptions.reporters,
tags: filterOptions.tags,
status: filterOptions.status,
+ owner: filterOptions.owner,
}}
setFilterRefetch={setFilterRefetch}
hiddenStatuses={hiddenStatuses}
diff --git a/x-pack/plugins/cases/public/components/all_cases/columns.tsx b/x-pack/plugins/cases/public/components/all_cases/columns.tsx
index 4ff90c20c9d60..663779029fcec 100644
--- a/x-pack/plugins/cases/public/components/all_cases/columns.tsx
+++ b/x-pack/plugins/cases/public/components/all_cases/columns.tsx
@@ -30,6 +30,7 @@ import {
CommentRequestAlertType,
ActionConnector,
} from '../../../common/api';
+import { OWNER_INFO } from '../../../common/constants';
import { getEmptyTagValue } from '../empty_value';
import { FormattedRelativePreferenceDate } from '../formatted_date';
import { CaseDetailsLink } from '../links';
@@ -45,6 +46,7 @@ import { StatusContextMenu } from '../case_action_bar/status_context_menu';
import { TruncatedText } from '../truncated_text';
import { getConnectorIcon } from '../utils';
import { PostComment } from '../../containers/use_post_comment';
+import type { CasesOwners } from '../../methods/can_use_cases';
export type CasesColumns =
| EuiTableActionsColumnType
@@ -79,6 +81,7 @@ export interface GetCasesColumn {
alertData?: Omit;
postComment?: (args: PostComment) => Promise;
updateCase?: (newCase: Case) => void;
+ showSolutionColumn?: boolean;
}
export const useCasesColumns = ({
dispatchUpdateCaseProperty,
@@ -93,6 +96,7 @@ export const useCasesColumns = ({
alertData,
postComment,
updateCase,
+ showSolutionColumn,
}: GetCasesColumn): CasesColumns[] => {
// Delete case
const {
@@ -251,6 +255,23 @@ export const useCasesColumns = ({
? renderStringField(`${totalAlerts}`, `case-table-column-alertsCount`)
: getEmptyTagValue(),
},
+ ...(showSolutionColumn
+ ? [
+ {
+ align: RIGHT_ALIGNMENT,
+ field: 'owner',
+ name: i18n.SOLUTION,
+ render: (caseOwner: CasesOwners) => {
+ const ownerInfo = OWNER_INFO[caseOwner];
+ return ownerInfo ? (
+
+ ) : (
+ getEmptyTagValue()
+ );
+ },
+ },
+ ]
+ : []),
{
align: RIGHT_ALIGNMENT,
field: 'totalComment',
diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx
index 2d14ffe5738ca..8089c85ee578b 100644
--- a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx
+++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx
@@ -9,6 +9,7 @@ import React from 'react';
import { mount } from 'enzyme';
import { CaseStatuses } from '../../../common/api';
+import { OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { TestProviders } from '../../common/mock';
import { useGetTags } from '../../containers/use_get_tags';
import { useGetReporters } from '../../containers/use_get_reporters';
@@ -30,6 +31,7 @@ const props = {
onFilterChanged,
initial: DEFAULT_FILTER_OPTIONS,
setFilterRefetch,
+ availableSolutions: [],
};
describe('CasesTableFilters ', () => {
@@ -168,4 +170,50 @@ describe('CasesTableFilters ', () => {
}
);
});
+
+ describe('dynamic Solution filter', () => {
+ it('shows Solution filter when provided more than 1 availableSolutions', () => {
+ const wrapper = mount(
+
+
+
+ );
+ expect(
+ wrapper.find(`[data-test-subj="options-filter-popover-button-Solution"]`).exists()
+ ).toBeTruthy();
+ });
+
+ it('does not show Solution filter when provided less than 1 availableSolutions', () => {
+ const wrapper = mount(
+
+
+
+ );
+ expect(
+ wrapper.find(`[data-test-subj="options-filter-popover-button-Solution"]`).exists()
+ ).toBeFalsy();
+ });
+ });
+
+ it('should call onFilterChange when selected solution changes', () => {
+ const wrapper = mount(
+
+
+
+ );
+ wrapper
+ .find(`[data-test-subj="options-filter-popover-button-Solution"]`)
+ .last()
+ .simulate('click');
+
+ wrapper.find(`[data-test-subj="options-filter-popover-item-0"]`).last().simulate('click');
+
+ expect(onFilterChanged).toBeCalledWith({ owner: [SECURITY_SOLUTION_OWNER] });
+ });
});
diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx
index e1ed709e0d93f..f75cebf88933c 100644
--- a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx
+++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx
@@ -27,6 +27,7 @@ interface CasesTableFiltersProps {
initial: FilterOptions;
setFilterRefetch: (val: () => void) => void;
hiddenStatuses?: CaseStatusWithAllStatus[];
+ availableSolutions: string[];
}
// Fix the width of the status dropdown to prevent hiding long text items
@@ -48,6 +49,7 @@ const defaultInitial = {
reporters: [],
status: StatusAll,
tags: [],
+ owner: [],
};
const CasesTableFiltersComponent = ({
@@ -58,12 +60,14 @@ const CasesTableFiltersComponent = ({
initial = defaultInitial,
setFilterRefetch,
hiddenStatuses,
+ availableSolutions,
}: CasesTableFiltersProps) => {
const [selectedReporters, setSelectedReporters] = useState(
initial.reporters.map((r) => r.full_name ?? r.username ?? '')
);
const [search, setSearch] = useState(initial.search);
const [selectedTags, setSelectedTags] = useState(initial.tags);
+ const [selectedOwner, setSelectedOwner] = useState(initial.owner);
const { tags, fetchTags } = useGetTags();
const { reporters, respReporters, fetchReporters } = useGetReporters();
@@ -108,6 +112,16 @@ const CasesTableFiltersComponent = ({
[onFilterChanged, selectedTags]
);
+ const handleSelectedSolution = useCallback(
+ (newOwner) => {
+ if (!isEqual(newOwner, selectedOwner) && newOwner.length) {
+ setSelectedOwner(newOwner);
+ onFilterChanged({ owner: newOwner });
+ }
+ },
+ [onFilterChanged, selectedOwner]
+ );
+
useEffect(() => {
if (selectedTags.length) {
const newTags = selectedTags.filter((t) => tags.includes(t));
@@ -183,6 +197,14 @@ const CasesTableFiltersComponent = ({
options={tags}
optionsEmptyLabel={i18n.NO_TAGS_AVAILABLE}
/>
+ {availableSolutions.length > 1 && (
+
+ )}
diff --git a/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts b/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts
index 32229d322162b..3475cae722e43 100644
--- a/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts
+++ b/x-pack/plugins/cases/public/components/app/use_available_owners.test.ts
@@ -6,9 +6,8 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import { SECURITY_SOLUTION_OWNER } from '../../../common';
-import { OBSERVABILITY_OWNER } from '../../../common/constants';
+import { OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { useKibana } from '../../common/lib/kibana';
import { useAvailableCasesOwners } from './use_available_owners';
diff --git a/x-pack/plugins/cases/public/components/filter_popover/index.tsx b/x-pack/plugins/cases/public/components/filter_popover/index.tsx
index 91cd7bfd57fa0..c28d00b08912a 100644
--- a/x-pack/plugins/cases/public/components/filter_popover/index.tsx
+++ b/x-pack/plugins/cases/public/components/filter_popover/index.tsx
@@ -21,7 +21,7 @@ interface FilterPopoverProps {
buttonLabel: string;
onSelectedOptionsChanged: Dispatch>;
options: string[];
- optionsEmptyLabel: string;
+ optionsEmptyLabel?: string;
selectedOptions: string[];
}
@@ -99,7 +99,7 @@ export const FilterPopoverComponent = ({
))}
- {options.length === 0 && (
+ {options.length === 0 && optionsEmptyLabel != null && (
diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx
index 99fbf48665138..fd7b2eddfe7c9 100644
--- a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx
+++ b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx
@@ -61,7 +61,7 @@ describe('useGetCases', () => {
});
await waitForNextUpdate();
expect(spyOnGetCases).toBeCalledWith({
- filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] },
+ filterOptions: { ...DEFAULT_FILTER_OPTIONS },
queryParams: DEFAULT_QUERY_PARAMS,
signal: abortCtrl.signal,
});
@@ -174,6 +174,7 @@ describe('useGetCases', () => {
search: 'new',
tags: ['new'],
status: CaseStatuses.closed,
+ owner: [SECURITY_SOLUTION_OWNER],
};
const { result, waitForNextUpdate } = renderHook(() => useGetCases(), {
@@ -212,7 +213,7 @@ describe('useGetCases', () => {
await waitForNextUpdate();
expect(spyOnGetCases.mock.calls[1][0]).toEqual({
- filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [SECURITY_SOLUTION_OWNER] },
+ filterOptions: { ...DEFAULT_FILTER_OPTIONS },
queryParams: {
...DEFAULT_QUERY_PARAMS,
...newQueryParams,
diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx
index a54a4183f766a..eacad3c8ca020 100644
--- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx
+++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx
@@ -19,7 +19,6 @@ import {
import { useToasts } from '../common/lib/kibana';
import * as i18n from './translations';
import { getCases, patchCase } from './api';
-import { useCasesContext } from '../components/cases_context/use_cases_context';
export interface UseGetCasesState {
data: AllCases;
@@ -106,6 +105,7 @@ export const DEFAULT_FILTER_OPTIONS: FilterOptions = {
status: StatusAll,
tags: [],
onlyCollectionType: false,
+ owner: [],
};
export const DEFAULT_QUERY_PARAMS: QueryParams = {
@@ -145,7 +145,6 @@ export const useGetCases = (
initialFilterOptions?: Partial;
} = {}
): UseGetCases => {
- const { owner } = useCasesContext();
const { initialQueryParams = empty, initialFilterOptions = empty } = params;
const [state, dispatch] = useReducer(dataFetchReducer, {
data: initialData,
@@ -185,7 +184,7 @@ export const useGetCases = (
dispatch({ type: 'FETCH_INIT', payload: 'cases' });
const response = await getCases({
- filterOptions: { ...filterOptions, owner },
+ filterOptions,
queryParams,
signal: abortCtrlFetchCases.current.signal,
});
@@ -208,7 +207,7 @@ export const useGetCases = (
}
}
},
- [owner, toasts]
+ [toasts]
);
const dispatchUpdateCaseProperty = useCallback(
diff --git a/x-pack/plugins/cases/public/methods/can_use_cases.test.ts b/x-pack/plugins/cases/public/methods/can_use_cases.test.ts
new file mode 100644
index 0000000000000..d4906c2702146
--- /dev/null
+++ b/x-pack/plugins/cases/public/methods/can_use_cases.test.ts
@@ -0,0 +1,145 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ApplicationStart } from 'kibana/public';
+import { canUseCases } from './can_use_cases';
+
+type CasesCapabilities = Pick<
+ ApplicationStart['capabilities'],
+ 'securitySolutionCases' | 'observabilityCases'
+>;
+
+const hasAll: CasesCapabilities = {
+ securitySolutionCases: {
+ crud_cases: true,
+ read_cases: true,
+ },
+ observabilityCases: {
+ crud_cases: true,
+ read_cases: true,
+ },
+};
+
+const hasNone: CasesCapabilities = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+ observabilityCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+};
+
+const hasSecurity = {
+ securitySolutionCases: {
+ crud_cases: true,
+ read_cases: true,
+ },
+ observabilityCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+};
+
+const hasObservability = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+ observabilityCases: {
+ crud_cases: true,
+ read_cases: true,
+ },
+};
+
+const hasObservabilityCrudTrue = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+ observabilityCases: {
+ crud_cases: true,
+ read_cases: false,
+ },
+};
+
+const hasSecurityCrudTrue = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+ observabilityCases: {
+ crud_cases: true,
+ read_cases: false,
+ },
+};
+
+const hasObservabilityReadTrue = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+ observabilityCases: {
+ crud_cases: false,
+ read_cases: true,
+ },
+};
+
+const hasSecurityReadTrue = {
+ securitySolutionCases: {
+ crud_cases: false,
+ read_cases: true,
+ },
+ observabilityCases: {
+ crud_cases: false,
+ read_cases: false,
+ },
+};
+
+const hasSecurityAsCrudAndObservabilityAsRead = {
+ securitySolutionCases: {
+ crud_cases: true,
+ },
+ observabilityCases: {
+ read_cases: true,
+ },
+};
+
+describe('canUseCases', () => {
+ it.each([hasAll, hasSecurity, hasObservability, hasSecurityAsCrudAndObservabilityAsRead])(
+ 'returns true for both crud and read, if a user has access to both on any solution',
+ (capability) => {
+ const permissions = canUseCases(capability)();
+ expect(permissions).toStrictEqual({ crud: true, read: true });
+ }
+ );
+
+ it.each([hasObservabilityCrudTrue, hasSecurityCrudTrue])(
+ 'returns true for only crud, if a user has access to only crud on any solution',
+ (capability) => {
+ const permissions = canUseCases(capability)();
+ expect(permissions).toStrictEqual({ crud: true, read: false });
+ }
+ );
+
+ it.each([hasObservabilityReadTrue, hasSecurityReadTrue])(
+ 'returns true for only read, if a user has access to only read on any solution',
+ (capability) => {
+ const permissions = canUseCases(capability)();
+ expect(permissions).toStrictEqual({ crud: false, read: true });
+ }
+ );
+
+ it.each([hasNone, {}])(
+ 'returns false for both, if a user has access to no solution',
+ (capability) => {
+ const permissions = canUseCases(capability)();
+ expect(permissions).toStrictEqual({ crud: false, read: false });
+ }
+ );
+});
diff --git a/x-pack/plugins/cases/public/methods/can_use_cases.ts b/x-pack/plugins/cases/public/methods/can_use_cases.ts
new file mode 100644
index 0000000000000..d0b83241963d8
--- /dev/null
+++ b/x-pack/plugins/cases/public/methods/can_use_cases.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ApplicationStart } from 'kibana/public';
+import { OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER } from '../../common/constants';
+
+export type CasesOwners = typeof SECURITY_SOLUTION_OWNER | typeof OBSERVABILITY_OWNER;
+
+/*
+ * Returns an object denoting the current user's ability to read and crud cases.
+ * If any owner(securitySolution, Observability) is found with crud or read capability respectively,
+ * then crud or read is set to true.
+ * Permissions for a specific owners can be found by passing an owner array
+ */
+
+export const canUseCases =
+ (capabilities: Partial) =>
+ (
+ owners: CasesOwners[] = [OBSERVABILITY_OWNER, SECURITY_SOLUTION_OWNER]
+ ): { crud: boolean; read: boolean } => ({
+ crud:
+ (capabilities && owners.some((owner) => capabilities[`${owner}Cases`]?.crud_cases)) ?? false,
+ read:
+ (capabilities && owners.some((owner) => capabilities[`${owner}Cases`]?.read_cases)) ?? false,
+ });
diff --git a/x-pack/plugins/cases/public/methods/index.ts b/x-pack/plugins/cases/public/methods/index.ts
index ee62ddaae7ce5..375bd42ee8581 100644
--- a/x-pack/plugins/cases/public/methods/index.ts
+++ b/x-pack/plugins/cases/public/methods/index.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+export * from './can_use_cases';
export * from './get_cases';
export * from './get_recent_cases';
export * from './get_all_cases_selector_modal';
diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts
index f2c7984f1469e..6f508d9b6da3b 100644
--- a/x-pack/plugins/cases/public/mocks.ts
+++ b/x-pack/plugins/cases/public/mocks.ts
@@ -8,6 +8,7 @@
import { CasesUiStart } from './types';
const createStartContract = (): jest.Mocked => ({
+ canUseCases: jest.fn(),
getCases: jest.fn(),
getAllCasesSelectorModal: jest.fn(),
getCreateCaseFlyout: jest.fn(),
diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts
index 48371f65b49e1..f38b2d12e2ad4 100644
--- a/x-pack/plugins/cases/public/plugin.ts
+++ b/x-pack/plugins/cases/public/plugin.ts
@@ -14,6 +14,7 @@ import {
getRecentCasesLazy,
getAllCasesSelectorModalLazy,
getCreateCaseFlyoutLazy,
+ canUseCases,
} from './methods';
import { CasesUiConfigType } from '../common/ui/types';
import { ENABLE_CASE_CONNECTOR } from '../common/constants';
@@ -38,6 +39,7 @@ export class CasesUiPlugin implements Plugin();
KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion, config });
return {
+ canUseCases: canUseCases(core.application.capabilities),
getCases: getCasesLazy,
getRecentCases: getRecentCasesLazy,
getCreateCaseFlyout: getCreateCaseFlyoutLazy,
diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts
index 1e995db3caa31..cb2e570b58e13 100644
--- a/x-pack/plugins/cases/public/types.ts
+++ b/x-pack/plugins/cases/public/types.ts
@@ -8,8 +8,8 @@
import { CoreStart } from 'kibana/public';
import { ReactElement } from 'react';
-import { LensPublicStart } from '../../lens/public';
-import { SecurityPluginSetup } from '../../security/public';
+import type { LensPublicStart } from '../../lens/public';
+import type { SecurityPluginSetup } from '../../security/public';
import type {
TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup,
TriggersAndActionsUIPublicPluginStart as TriggersActionsStart,
@@ -19,11 +19,12 @@ import type { EmbeddableStart } from '../../../../src/plugins/embeddable/public'
import type { SpacesPluginStart } from '../../spaces/public';
import type { Storage } from '../../../../src/plugins/kibana_utils/public';
-import {
+import type {
GetCasesProps,
GetAllCasesSelectorModalProps,
GetCreateCaseFlyoutProps,
GetRecentCasesProps,
+ CasesOwners,
} from './methods';
export interface SetupPlugins {
@@ -52,6 +53,15 @@ export type StartServices = CoreStart &
};
export interface CasesUiStart {
+ /**
+ * Returns an object denoting the current user's ability to read and crud cases.
+ * If any owner(securitySolution, Observability) is found with crud or read capability respectively,
+ * then crud or read is set to true.
+ * Permissions for specific owners can be found by passing an owner array
+ * @param owners an array of CaseOwners that should be queried for permission
+ * @returns An object denoting the case permissions of the current user
+ */
+ canUseCases: (owners?: CasesOwners[]) => { crud: boolean; read: boolean };
/**
* Get cases
* @param props GetCasesProps