mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Cases] Enable Cases on the stack management page (#125224)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
de4f3e204c
commit
494047a2c0
27 changed files with 531 additions and 108 deletions
|
@ -13,6 +13,12 @@ import {
|
|||
} from './constants/saved_objects';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
|
||||
|
||||
/**
|
||||
* The order of appearance in the feature privilege page
|
||||
* under the management section.
|
||||
*/
|
||||
const FEATURE_ORDER = 3000;
|
||||
|
||||
export const ACTIONS_FEATURE = {
|
||||
id: 'actions',
|
||||
name: i18n.translate('xpack.actions.featureRegistry.actionsFeatureName', {
|
||||
|
@ -20,6 +26,7 @@ export const ACTIONS_FEATURE = {
|
|||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
app: [],
|
||||
order: FEATURE_ORDER,
|
||||
management: {
|
||||
insightsAndAlerting: ['triggersActions'],
|
||||
},
|
||||
|
|
|
@ -7,16 +7,33 @@
|
|||
import { ConnectorTypes } from './api';
|
||||
import { CasesContextFeatures } from './ui/types';
|
||||
|
||||
export const DEFAULT_DATE_FORMAT = 'dateFormat';
|
||||
export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz';
|
||||
export const DEFAULT_DATE_FORMAT = 'dateFormat' as const;
|
||||
export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz' as const;
|
||||
|
||||
export const APP_ID = 'cases';
|
||||
/**
|
||||
* Application
|
||||
*/
|
||||
|
||||
export const CASE_SAVED_OBJECT = 'cases';
|
||||
export const CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT = 'cases-connector-mappings';
|
||||
export const CASE_USER_ACTION_SAVED_OBJECT = 'cases-user-actions';
|
||||
export const CASE_COMMENT_SAVED_OBJECT = 'cases-comments';
|
||||
export const CASE_CONFIGURE_SAVED_OBJECT = 'cases-configure';
|
||||
export const APP_ID = 'cases' as const;
|
||||
export const FEATURE_ID = 'generalCases' as const;
|
||||
export const APP_OWNER = 'cases' as const;
|
||||
export const APP_PATH = '/app/management/insightsAndAlerting/cases' as const;
|
||||
/**
|
||||
* The main Cases application is in the stack management under the
|
||||
* Alerts and Insights section. To do that, Cases registers to the management
|
||||
* application. This constant holds the application ID of the management plugin
|
||||
*/
|
||||
export const STACK_APP_ID = 'management' as const;
|
||||
|
||||
/**
|
||||
* Saved objects
|
||||
*/
|
||||
|
||||
export const CASE_SAVED_OBJECT = 'cases' as const;
|
||||
export const CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT = 'cases-connector-mappings' as const;
|
||||
export const CASE_USER_ACTION_SAVED_OBJECT = 'cases-user-actions' as const;
|
||||
export const CASE_COMMENT_SAVED_OBJECT = 'cases-comments' as const;
|
||||
export const CASE_CONFIGURE_SAVED_OBJECT = 'cases-configure' as const;
|
||||
|
||||
/**
|
||||
* If more values are added here please also add them here: x-pack/test/cases_api_integration/common/fixtures/plugins
|
||||
|
@ -33,32 +50,32 @@ export const SAVED_OBJECT_TYPES = [
|
|||
* Case routes
|
||||
*/
|
||||
|
||||
export const CASES_URL = '/api/cases';
|
||||
export const CASE_DETAILS_URL = `${CASES_URL}/{case_id}`;
|
||||
export const CASE_CONFIGURE_URL = `${CASES_URL}/configure`;
|
||||
export const CASE_CONFIGURE_DETAILS_URL = `${CASES_URL}/configure/{configuration_id}`;
|
||||
export const CASE_CONFIGURE_CONNECTORS_URL = `${CASE_CONFIGURE_URL}/connectors`;
|
||||
export const CASES_URL = '/api/cases' as const;
|
||||
export const CASE_DETAILS_URL = `${CASES_URL}/{case_id}` as const;
|
||||
export const CASE_CONFIGURE_URL = `${CASES_URL}/configure` as const;
|
||||
export const CASE_CONFIGURE_DETAILS_URL = `${CASES_URL}/configure/{configuration_id}` as const;
|
||||
export const CASE_CONFIGURE_CONNECTORS_URL = `${CASE_CONFIGURE_URL}/connectors` as const;
|
||||
|
||||
export const CASE_COMMENTS_URL = `${CASE_DETAILS_URL}/comments`;
|
||||
export const CASE_COMMENT_DETAILS_URL = `${CASE_DETAILS_URL}/comments/{comment_id}`;
|
||||
export const CASE_PUSH_URL = `${CASE_DETAILS_URL}/connector/{connector_id}/_push`;
|
||||
export const CASE_REPORTERS_URL = `${CASES_URL}/reporters`;
|
||||
export const CASE_STATUS_URL = `${CASES_URL}/status`;
|
||||
export const CASE_TAGS_URL = `${CASES_URL}/tags`;
|
||||
export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions`;
|
||||
export const CASE_COMMENTS_URL = `${CASE_DETAILS_URL}/comments` as const;
|
||||
export const CASE_COMMENT_DETAILS_URL = `${CASE_DETAILS_URL}/comments/{comment_id}` as const;
|
||||
export const CASE_PUSH_URL = `${CASE_DETAILS_URL}/connector/{connector_id}/_push` as const;
|
||||
export const CASE_REPORTERS_URL = `${CASES_URL}/reporters` as const;
|
||||
export const CASE_STATUS_URL = `${CASES_URL}/status` as const;
|
||||
export const CASE_TAGS_URL = `${CASES_URL}/tags` as const;
|
||||
export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions` as const;
|
||||
|
||||
export const CASE_ALERTS_URL = `${CASES_URL}/alerts/{alert_id}`;
|
||||
export const CASE_DETAILS_ALERTS_URL = `${CASE_DETAILS_URL}/alerts`;
|
||||
export const CASE_ALERTS_URL = `${CASES_URL}/alerts/{alert_id}` as const;
|
||||
export const CASE_DETAILS_ALERTS_URL = `${CASE_DETAILS_URL}/alerts` as const;
|
||||
|
||||
export const CASE_METRICS_DETAILS_URL = `${CASES_URL}/metrics/{case_id}`;
|
||||
export const CASE_METRICS_DETAILS_URL = `${CASES_URL}/metrics/{case_id}` as const;
|
||||
|
||||
/**
|
||||
* Action routes
|
||||
*/
|
||||
|
||||
export const ACTION_URL = '/api/actions';
|
||||
export const ACTION_TYPES_URL = `${ACTION_URL}/connector_types`;
|
||||
export const CONNECTORS_URL = `${ACTION_URL}/connectors`;
|
||||
export const ACTION_URL = '/api/actions' as const;
|
||||
export const ACTION_TYPES_URL = `${ACTION_URL}/connector_types` as const;
|
||||
export const CONNECTORS_URL = `${ACTION_URL}/connectors` as const;
|
||||
|
||||
export const SUPPORTED_CONNECTORS = [
|
||||
`${ConnectorTypes.serviceNowITSM}`,
|
||||
|
@ -71,10 +88,10 @@ export const SUPPORTED_CONNECTORS = [
|
|||
/**
|
||||
* Alerts
|
||||
*/
|
||||
export const MAX_ALERTS_PER_CASE = 5000;
|
||||
export const MAX_ALERTS_PER_CASE = 5000 as const;
|
||||
|
||||
export const SECURITY_SOLUTION_OWNER = 'securitySolution';
|
||||
export const OBSERVABILITY_OWNER = 'observability';
|
||||
export const SECURITY_SOLUTION_OWNER = 'securitySolution' as const;
|
||||
export const OBSERVABILITY_OWNER = 'observability' as const;
|
||||
|
||||
export const OWNER_INFO = {
|
||||
[SECURITY_SOLUTION_OWNER]: {
|
||||
|
@ -85,16 +102,16 @@ export const OWNER_INFO = {
|
|||
label: 'Observability',
|
||||
iconType: 'logoObservability',
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
export const MAX_DOCS_PER_PAGE = 10000;
|
||||
export const MAX_CONCURRENT_SEARCHES = 10;
|
||||
export const MAX_DOCS_PER_PAGE = 10000 as const;
|
||||
export const MAX_CONCURRENT_SEARCHES = 10 as const;
|
||||
|
||||
/**
|
||||
* Validation
|
||||
*/
|
||||
|
||||
export const MAX_TITLE_LENGTH = 64;
|
||||
export const MAX_TITLE_LENGTH = 64 as const;
|
||||
|
||||
/**
|
||||
* Cases features
|
||||
|
|
|
@ -10,8 +10,10 @@
|
|||
"id":"cases",
|
||||
"kibanaVersion":"kibana",
|
||||
"optionalPlugins":[
|
||||
"home",
|
||||
"security",
|
||||
"spaces",
|
||||
"features",
|
||||
"usageCollection"
|
||||
],
|
||||
"owner":{
|
||||
|
@ -20,14 +22,18 @@
|
|||
},
|
||||
"requiredPlugins":[
|
||||
"actions",
|
||||
"data",
|
||||
"embeddable",
|
||||
"esUiShared",
|
||||
"lens",
|
||||
"features",
|
||||
"kibanaReact",
|
||||
"kibanaUtils",
|
||||
"triggersActionsUi"
|
||||
"triggersActionsUi",
|
||||
"management"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"home",
|
||||
"savedObjects"
|
||||
],
|
||||
"server":true,
|
||||
|
|
72
x-pack/plugins/cases/public/application.tsx
Normal file
72
x-pack/plugins/cases/public/application.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { EuiErrorBoundary } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
KibanaThemeProvider,
|
||||
useUiSetting$,
|
||||
} from '../../../../src/plugins/kibana_react/public';
|
||||
import { EuiThemeProvider as StyledComponentsThemeProvider } from '../../../../src/plugins/kibana_react/common';
|
||||
import { RenderAppProps } from './types';
|
||||
import { CasesApp } from './components/app';
|
||||
|
||||
export const renderApp = (deps: RenderAppProps) => {
|
||||
const { mountParams } = deps;
|
||||
const { element } = mountParams;
|
||||
|
||||
ReactDOM.render(<App deps={deps} />, element);
|
||||
|
||||
return () => {
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
};
|
||||
|
||||
const CasesAppWithContext = () => {
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
|
||||
return (
|
||||
<StyledComponentsThemeProvider darkMode={darkMode}>
|
||||
<CasesApp />
|
||||
</StyledComponentsThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
CasesAppWithContext.displayName = 'CasesAppWithContext';
|
||||
|
||||
export const App: React.FC<{ deps: RenderAppProps }> = ({ deps }) => {
|
||||
const { mountParams, coreStart, pluginsStart, storage, kibanaVersion } = deps;
|
||||
const { history, theme$ } = mountParams;
|
||||
|
||||
return (
|
||||
<EuiErrorBoundary>
|
||||
<I18nProvider>
|
||||
<KibanaThemeProvider theme$={theme$}>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
kibanaVersion,
|
||||
...coreStart,
|
||||
...pluginsStart,
|
||||
storage,
|
||||
}}
|
||||
>
|
||||
<Router history={history}>
|
||||
<CasesAppWithContext />
|
||||
</Router>
|
||||
</KibanaContextProvider>
|
||||
</KibanaThemeProvider>
|
||||
</I18nProvider>
|
||||
</EuiErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
App.displayName = 'App';
|
43
x-pack/plugins/cases/public/common/hooks.test.tsx
Normal file
43
x-pack/plugins/cases/public/common/hooks.test.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { TestProviders } from '../common/mock';
|
||||
import { useIsMainApplication } from './hooks';
|
||||
import { useApplication } from '../components/cases_context/use_application';
|
||||
|
||||
jest.mock('../components/cases_context/use_application');
|
||||
|
||||
const useApplicationMock = useApplication as jest.Mock;
|
||||
|
||||
describe('hooks', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
useApplicationMock.mockReturnValue({ appId: 'management', appTitle: 'Management' });
|
||||
});
|
||||
|
||||
describe('useIsMainApplication', () => {
|
||||
it('returns true if it is the main application', () => {
|
||||
const { result } = renderHook(() => useIsMainApplication(), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
|
||||
expect(result.current).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if it is not the main application', () => {
|
||||
useApplicationMock.mockReturnValue({ appId: 'testAppId', appTitle: 'Test app' });
|
||||
const { result } = renderHook(() => useIsMainApplication(), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
15
x-pack/plugins/cases/public/common/hooks.ts
Normal file
15
x-pack/plugins/cases/public/common/hooks.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { STACK_APP_ID } from '../../common/constants';
|
||||
import { useCasesContext } from '../components/cases_context/use_cases_context';
|
||||
|
||||
export const useIsMainApplication = () => {
|
||||
const { appId } = useCasesContext();
|
||||
|
||||
return appId === STACK_APP_ID;
|
||||
};
|
|
@ -10,7 +10,11 @@ import moment from 'moment-timezone';
|
|||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ } from '../../../../common/constants';
|
||||
import {
|
||||
FEATURE_ID,
|
||||
DEFAULT_DATE_FORMAT,
|
||||
DEFAULT_DATE_FORMAT_TZ,
|
||||
} from '../../../../common/constants';
|
||||
import { AuthenticatedUser } from '../../../../../security/common/model';
|
||||
import { convertToCamelCase } from '../../../containers/utils';
|
||||
import { StartServices } from '../../../types';
|
||||
|
@ -155,3 +159,17 @@ export const useNavigation = (appId: string) => {
|
|||
const { getAppUrl } = useAppUrl(appId);
|
||||
return { navigateTo, getAppUrl };
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the capabilities of the main cases application
|
||||
*
|
||||
*/
|
||||
export const useApplicationCapabilities = (): { crud: boolean; read: boolean } => {
|
||||
const capabilities = useKibana().services.application.capabilities;
|
||||
const casesCapabilities = capabilities[FEATURE_ID];
|
||||
|
||||
return {
|
||||
crud: !!casesCapabilities?.crud_cases,
|
||||
read: !!casesCapabilities?.read_cases,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React from 'react';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { APP_ID } from '../../../common/constants';
|
||||
import { useNavigation } from '../../common/lib/kibana';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import {
|
||||
|
@ -33,9 +34,12 @@ describe('hooks', () => {
|
|||
|
||||
describe('useCasesNavigation', () => {
|
||||
it('it calls getAppUrl with correct arguments', () => {
|
||||
const { result } = renderHook(() => useCasesNavigation(CasesDeepLinkId.cases), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
const { result } = renderHook(
|
||||
() => useCasesNavigation({ deepLinkId: CasesDeepLinkId.cases }),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
}
|
||||
);
|
||||
|
||||
const [getCasesUrl] = result.current;
|
||||
|
||||
|
@ -43,20 +47,23 @@ describe('hooks', () => {
|
|||
getCasesUrl(false);
|
||||
});
|
||||
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, deepLinkId: 'cases' });
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, deepLinkId: APP_ID });
|
||||
});
|
||||
|
||||
it('it calls navigateToAllCases with correct arguments', () => {
|
||||
const { result } = renderHook(() => useCasesNavigation(CasesDeepLinkId.cases), {
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
});
|
||||
const { result } = renderHook(
|
||||
() => useCasesNavigation({ deepLinkId: CasesDeepLinkId.cases }),
|
||||
{
|
||||
wrapper: ({ children }) => <TestProviders>{children}</TestProviders>,
|
||||
}
|
||||
);
|
||||
const [, navigateToCases] = result.current;
|
||||
|
||||
act(() => {
|
||||
navigateToCases();
|
||||
});
|
||||
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: 'cases' });
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: APP_ID });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -70,7 +77,7 @@ describe('hooks', () => {
|
|||
result.current.getAllCasesUrl(false);
|
||||
});
|
||||
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, deepLinkId: 'cases' });
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, path: '/', deepLinkId: APP_ID });
|
||||
});
|
||||
|
||||
it('it calls navigateToAllCases with correct arguments', () => {
|
||||
|
@ -82,7 +89,7 @@ describe('hooks', () => {
|
|||
result.current.navigateToAllCases();
|
||||
});
|
||||
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: 'cases' });
|
||||
expect(navigateTo).toHaveBeenCalledWith({ path: '/', deepLinkId: APP_ID });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -96,7 +103,11 @@ describe('hooks', () => {
|
|||
result.current.getCreateCaseUrl(false);
|
||||
});
|
||||
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, deepLinkId: 'cases_create' });
|
||||
expect(getAppUrl).toHaveBeenCalledWith({
|
||||
absolute: false,
|
||||
path: '/create',
|
||||
deepLinkId: APP_ID,
|
||||
});
|
||||
});
|
||||
|
||||
it('it calls navigateToAllCases with correct arguments', () => {
|
||||
|
@ -108,7 +119,7 @@ describe('hooks', () => {
|
|||
result.current.navigateToCreateCase();
|
||||
});
|
||||
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: 'cases_create' });
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: APP_ID, path: '/create' });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -122,7 +133,11 @@ describe('hooks', () => {
|
|||
result.current.getConfigureCasesUrl(false);
|
||||
});
|
||||
|
||||
expect(getAppUrl).toHaveBeenCalledWith({ absolute: false, deepLinkId: 'cases_configure' });
|
||||
expect(getAppUrl).toHaveBeenCalledWith({
|
||||
absolute: false,
|
||||
path: '/configure',
|
||||
deepLinkId: APP_ID,
|
||||
});
|
||||
});
|
||||
|
||||
it('it calls navigateToAllCases with correct arguments', () => {
|
||||
|
@ -134,7 +149,7 @@ describe('hooks', () => {
|
|||
result.current.navigateToConfigureCases();
|
||||
});
|
||||
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: 'cases_configure' });
|
||||
expect(navigateTo).toHaveBeenCalledWith({ path: '/configure', deepLinkId: APP_ID });
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -150,7 +165,7 @@ describe('hooks', () => {
|
|||
|
||||
expect(getAppUrl).toHaveBeenCalledWith({
|
||||
absolute: false,
|
||||
deepLinkId: 'cases',
|
||||
deepLinkId: APP_ID,
|
||||
path: '/test',
|
||||
});
|
||||
});
|
||||
|
@ -164,7 +179,7 @@ describe('hooks', () => {
|
|||
result.current.navigateToCaseView({ detailName: 'test' });
|
||||
});
|
||||
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: 'cases', path: '/test' });
|
||||
expect(navigateTo).toHaveBeenCalledWith({ deepLinkId: APP_ID, path: '/test' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,10 +7,17 @@
|
|||
|
||||
import { useCallback } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { APP_ID } from '../../../common/constants';
|
||||
import { useNavigation } from '../lib/kibana';
|
||||
import { useCasesContext } from '../../components/cases_context/use_cases_context';
|
||||
import { CasesDeepLinkId, ICasesDeepLinkId } from './deep_links';
|
||||
import { CaseViewPathParams, generateCaseViewPath } from './paths';
|
||||
import { ICasesDeepLinkId } from './deep_links';
|
||||
import {
|
||||
CASES_CONFIGURE_PATH,
|
||||
CASES_CREATE_PATH,
|
||||
CaseViewPathParams,
|
||||
generateCaseViewPath,
|
||||
} from './paths';
|
||||
|
||||
export const useCaseViewParams = () => useParams<CaseViewPathParams>();
|
||||
|
||||
|
@ -18,34 +25,60 @@ type GetCasesUrl = (absolute?: boolean) => string;
|
|||
type NavigateToCases = () => void;
|
||||
type UseCasesNavigation = [GetCasesUrl, NavigateToCases];
|
||||
|
||||
export const useCasesNavigation = (deepLinkId: ICasesDeepLinkId): UseCasesNavigation => {
|
||||
export const useCasesNavigation = ({
|
||||
path,
|
||||
deepLinkId,
|
||||
}: {
|
||||
path?: string;
|
||||
deepLinkId?: ICasesDeepLinkId;
|
||||
}): UseCasesNavigation => {
|
||||
const { appId } = useCasesContext();
|
||||
const { navigateTo, getAppUrl } = useNavigation(appId);
|
||||
const getCasesUrl = useCallback<GetCasesUrl>(
|
||||
(absolute) => getAppUrl({ deepLinkId, absolute }),
|
||||
[getAppUrl, deepLinkId]
|
||||
(absolute) => getAppUrl({ path, deepLinkId, absolute }),
|
||||
[getAppUrl, deepLinkId, path]
|
||||
);
|
||||
const navigateToCases = useCallback<NavigateToCases>(
|
||||
() => navigateTo({ deepLinkId }),
|
||||
[navigateTo, deepLinkId]
|
||||
() => navigateTo({ path, deepLinkId }),
|
||||
[navigateTo, deepLinkId, path]
|
||||
);
|
||||
return [getCasesUrl, navigateToCases];
|
||||
};
|
||||
|
||||
/**
|
||||
* Cases can be either be part of a solution or a standalone application
|
||||
* The standalone application is registered from the cases plugin and is called
|
||||
* the main application. The main application uses paths and the solutions
|
||||
* deep links.
|
||||
*/
|
||||
const navigationMapping = {
|
||||
all: { path: '/' },
|
||||
create: { path: CASES_CREATE_PATH },
|
||||
configure: { path: CASES_CONFIGURE_PATH },
|
||||
};
|
||||
|
||||
export const useAllCasesNavigation = () => {
|
||||
const [getAllCasesUrl, navigateToAllCases] = useCasesNavigation(CasesDeepLinkId.cases);
|
||||
const [getAllCasesUrl, navigateToAllCases] = useCasesNavigation({
|
||||
path: navigationMapping.all.path,
|
||||
deepLinkId: APP_ID,
|
||||
});
|
||||
|
||||
return { getAllCasesUrl, navigateToAllCases };
|
||||
};
|
||||
|
||||
export const useCreateCaseNavigation = () => {
|
||||
const [getCreateCaseUrl, navigateToCreateCase] = useCasesNavigation(CasesDeepLinkId.casesCreate);
|
||||
const [getCreateCaseUrl, navigateToCreateCase] = useCasesNavigation({
|
||||
path: navigationMapping.create.path,
|
||||
deepLinkId: APP_ID,
|
||||
});
|
||||
return { getCreateCaseUrl, navigateToCreateCase };
|
||||
};
|
||||
|
||||
export const useConfigureCasesNavigation = () => {
|
||||
const [getConfigureCasesUrl, navigateToConfigureCases] = useCasesNavigation(
|
||||
CasesDeepLinkId.casesConfigure
|
||||
);
|
||||
const [getConfigureCasesUrl, navigateToConfigureCases] = useCasesNavigation({
|
||||
path: navigationMapping.configure.path,
|
||||
deepLinkId: APP_ID,
|
||||
});
|
||||
return { getConfigureCasesUrl, navigateToConfigureCases };
|
||||
};
|
||||
|
||||
|
@ -55,19 +88,25 @@ type NavigateToCaseView = (pathParams: CaseViewPathParams) => void;
|
|||
export const useCaseViewNavigation = () => {
|
||||
const { appId } = useCasesContext();
|
||||
const { navigateTo, getAppUrl } = useNavigation(appId);
|
||||
const deepLinkId = APP_ID;
|
||||
|
||||
const getCaseViewUrl = useCallback<GetCaseViewUrl>(
|
||||
(pathParams, absolute) =>
|
||||
getAppUrl({
|
||||
deepLinkId: CasesDeepLinkId.cases,
|
||||
deepLinkId,
|
||||
absolute,
|
||||
path: generateCaseViewPath(pathParams),
|
||||
}),
|
||||
[getAppUrl]
|
||||
[deepLinkId, getAppUrl]
|
||||
);
|
||||
|
||||
const navigateToCaseView = useCallback<NavigateToCaseView>(
|
||||
(pathParams) =>
|
||||
navigateTo({ deepLinkId: CasesDeepLinkId.cases, path: generateCaseViewPath(pathParams) }),
|
||||
[navigateTo]
|
||||
navigateTo({
|
||||
deepLinkId,
|
||||
path: generateCaseViewPath(pathParams),
|
||||
}),
|
||||
[navigateTo, deepLinkId]
|
||||
);
|
||||
return { getCaseViewUrl, navigateToCaseView };
|
||||
};
|
||||
|
|
|
@ -18,24 +18,40 @@ describe('Paths', () => {
|
|||
it('returns the correct path', () => {
|
||||
expect(getCreateCasePath('test')).toBe('test/create');
|
||||
});
|
||||
|
||||
it('normalize the path correctly', () => {
|
||||
expect(getCreateCasePath('//test//page')).toBe('/test/page/create');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCasesConfigurePath', () => {
|
||||
it('returns the correct path', () => {
|
||||
expect(getCasesConfigurePath('test')).toBe('test/configure');
|
||||
});
|
||||
|
||||
it('normalize the path correctly', () => {
|
||||
expect(getCasesConfigurePath('//test//page')).toBe('/test/page/configure');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCaseViewPath', () => {
|
||||
it('returns the correct path', () => {
|
||||
expect(getCaseViewPath('test')).toBe('test/:detailName');
|
||||
});
|
||||
|
||||
it('normalize the path correctly', () => {
|
||||
expect(getCaseViewPath('//test//page')).toBe('/test/page/:detailName');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCaseViewWithCommentPath', () => {
|
||||
it('returns the correct path', () => {
|
||||
expect(getCaseViewWithCommentPath('test')).toBe('test/:detailName/:commentId');
|
||||
});
|
||||
|
||||
it('normalize the path correctly', () => {
|
||||
expect(getCaseViewWithCommentPath('//test//page')).toBe('/test/page/:detailName/:commentId');
|
||||
});
|
||||
});
|
||||
|
||||
describe('generateCaseViewPath', () => {
|
||||
|
|
|
@ -18,12 +18,16 @@ export const CASES_CONFIGURE_PATH = '/configure' as const;
|
|||
export const CASE_VIEW_PATH = '/:detailName' as const;
|
||||
export const CASE_VIEW_COMMENT_PATH = `${CASE_VIEW_PATH}/:commentId` as const;
|
||||
|
||||
export const getCreateCasePath = (casesBasePath: string) => `${casesBasePath}${CASES_CREATE_PATH}`;
|
||||
const normalizePath = (path: string): string => path.replaceAll('//', '/');
|
||||
|
||||
export const getCreateCasePath = (casesBasePath: string) =>
|
||||
normalizePath(`${casesBasePath}${CASES_CREATE_PATH}`);
|
||||
export const getCasesConfigurePath = (casesBasePath: string) =>
|
||||
`${casesBasePath}${CASES_CONFIGURE_PATH}`;
|
||||
export const getCaseViewPath = (casesBasePath: string) => `${casesBasePath}${CASE_VIEW_PATH}`;
|
||||
normalizePath(`${casesBasePath}${CASES_CONFIGURE_PATH}`);
|
||||
export const getCaseViewPath = (casesBasePath: string) =>
|
||||
normalizePath(`${casesBasePath}${CASE_VIEW_PATH}`);
|
||||
export const getCaseViewWithCommentPath = (casesBasePath: string) =>
|
||||
`${casesBasePath}${CASE_VIEW_COMMENT_PATH}`;
|
||||
normalizePath(`${casesBasePath}${CASE_VIEW_COMMENT_PATH}`);
|
||||
|
||||
export const generateCaseViewPath = (params: CaseViewPathParams): string => {
|
||||
const { commentId } = params;
|
||||
|
@ -31,7 +35,7 @@ export const generateCaseViewPath = (params: CaseViewPathParams): string => {
|
|||
const pathParams = params as unknown as { [paramName: string]: string };
|
||||
|
||||
if (commentId) {
|
||||
return generatePath(CASE_VIEW_COMMENT_PATH, pathParams);
|
||||
return normalizePath(generatePath(CASE_VIEW_COMMENT_PATH, pathParams));
|
||||
}
|
||||
return generatePath(CASE_VIEW_PATH, pathParams);
|
||||
return normalizePath(generatePath(CASE_VIEW_PATH, pathParams));
|
||||
};
|
||||
|
|
|
@ -268,3 +268,11 @@ export const CASE_SUCCESS_SYNC_TEXT = i18n.translate('xpack.cases.actions.caseSu
|
|||
export const VIEW_CASE = i18n.translate('xpack.cases.actions.viewCase', {
|
||||
defaultMessage: 'View Case',
|
||||
});
|
||||
|
||||
export const APP_TITLE = i18n.translate('xpack.cases.common.appTitle', {
|
||||
defaultMessage: 'Cases',
|
||||
});
|
||||
|
||||
export const APP_DESC = i18n.translate('xpack.cases.common.appDescription', {
|
||||
defaultMessage: 'Open and track issues, push information to third party systems.',
|
||||
});
|
||||
|
|
|
@ -5,9 +5,33 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { CasesRoutes } from './routes';
|
||||
import React from 'react';
|
||||
import { APP_OWNER } from '../../../common/constants';
|
||||
import { useApplicationCapabilities } from '../../common/lib/kibana';
|
||||
|
||||
import { getCasesLazy } from '../../methods';
|
||||
import { Wrapper } from '../wrappers';
|
||||
import { CasesRoutesProps } from './types';
|
||||
|
||||
export type CasesProps = CasesRoutesProps;
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { CasesRoutes as default };
|
||||
|
||||
const CasesAppComponent: React.FC = () => {
|
||||
const userCapabilities = useApplicationCapabilities();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{getCasesLazy({
|
||||
owner: [APP_OWNER],
|
||||
useFetchAlertData: () => [false, {}],
|
||||
userCanCrud: userCapabilities.crud,
|
||||
basePath: '/',
|
||||
features: { alerts: { sync: false } },
|
||||
releasePhase: 'experimental',
|
||||
})}
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
CasesAppComponent.displayName = 'CasesApp';
|
||||
|
||||
export const CasesApp = React.memo(CasesAppComponent);
|
||||
|
|
|
@ -91,3 +91,5 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
|
|||
CasesRoutesComponent.displayName = 'CasesRoutes';
|
||||
|
||||
export const CasesRoutes = React.memo(CasesRoutesComponent);
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export { CasesRoutes as default };
|
||||
|
|
|
@ -7,21 +7,6 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const NO_PRIVILEGES_MSG = (pageName: string) =>
|
||||
i18n.translate('xpack.cases.noPrivileges.message', {
|
||||
values: { pageName },
|
||||
defaultMessage:
|
||||
'To view {pageName} page, you must update privileges. For more information, contact your Kibana administrator.',
|
||||
});
|
||||
|
||||
export const NO_PRIVILEGES_TITLE = i18n.translate('xpack.cases.noPrivileges.title', {
|
||||
defaultMessage: 'Privileges required',
|
||||
});
|
||||
|
||||
export const NO_PRIVILEGES_BUTTON = i18n.translate('xpack.cases.noPrivileges.button', {
|
||||
defaultMessage: 'Back to Cases',
|
||||
});
|
||||
|
||||
export const CREATE_CASE_PAGE_NAME = i18n.translate('xpack.cases.createCase', {
|
||||
defaultMessage: 'Create Case',
|
||||
});
|
||||
|
|
|
@ -36,6 +36,7 @@ import type { EmbeddablePackageState } from '../../../../../../../../src/plugins
|
|||
import { SavedObjectFinderUi } from './saved_objects_finder';
|
||||
import { useLensDraftComment } from './use_lens_draft_comment';
|
||||
import { VISUALIZATION } from './translations';
|
||||
import { useIsMainApplication } from '../../../../common/hooks';
|
||||
|
||||
const BetaBadgeWrapper = styled.span`
|
||||
display: inline-flex;
|
||||
|
@ -84,6 +85,7 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({
|
|||
const { draftComment, clearDraftComment } = useLensDraftComment();
|
||||
const commentEditorContext = useContext(CommentEditorContext);
|
||||
const markdownContext = useContext(EuiMarkdownContext);
|
||||
const isMainApplication = useIsMainApplication();
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
if (currentAppId) {
|
||||
|
@ -126,8 +128,11 @@ const LensEditorComponent: LensEuiMarkdownEditorUiPlugin['editor'] = ({
|
|||
);
|
||||
|
||||
const originatingPath = useMemo(
|
||||
() => `${location.pathname}${location.search}`,
|
||||
[location.pathname, location.search]
|
||||
() =>
|
||||
isMainApplication
|
||||
? `/insightsAndAlerting/cases${location.pathname}${location.search}`
|
||||
: `${location.pathname}${location.search}`,
|
||||
[isMainApplication, location.pathname, location.search]
|
||||
);
|
||||
|
||||
const handleCreateInLensClick = useCallback(() => {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { IconType } from '@elastic/eui';
|
||||
import { ConnectorTypes } from '../../common/api';
|
||||
import { FieldConfig, ValidationConfig } from '../common/shared_imports';
|
||||
import { StartPlugins } from '../types';
|
||||
import { CasesPluginStart } from '../types';
|
||||
import { connectorValidator as swimlaneConnectorValidator } from './connectors/swimlane/validator';
|
||||
import { connectorValidator as servicenowConnectorValidator } from './connectors/servicenow/validator';
|
||||
import { CaseActionConnector } from './types';
|
||||
|
@ -48,7 +48,7 @@ export const getConnectorsFormValidators = ({
|
|||
});
|
||||
|
||||
export const getConnectorIcon = (
|
||||
triggersActionsUi: StartPlugins['triggersActionsUi'],
|
||||
triggersActionsUi: CasesPluginStart['triggersActionsUi'],
|
||||
type?: string
|
||||
): IconType => {
|
||||
/**
|
||||
|
|
|
@ -27,3 +27,8 @@ export const ContentWrapper = styled.div`
|
|||
padding: ${theme.eui.paddingSizes.l} 0 ${gutterTimeline} 0;
|
||||
`};
|
||||
`;
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
|
|
@ -12,7 +12,8 @@ import { CasesProvider, CasesContextProps } from '../components/cases_context';
|
|||
|
||||
export type GetCasesProps = CasesProps & CasesContextProps;
|
||||
|
||||
const CasesLazy: React.FC<CasesProps> = lazy(() => import('../components/app'));
|
||||
const CasesRoutesLazy: React.FC<CasesProps> = lazy(() => import('../components/app/routes'));
|
||||
|
||||
export const getCasesLazy = ({
|
||||
owner,
|
||||
userCanCrud,
|
||||
|
@ -29,7 +30,7 @@ export const getCasesLazy = ({
|
|||
}: GetCasesProps) => (
|
||||
<CasesProvider value={{ owner, userCanCrud, basePath, features, releasePhase }}>
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<CasesLazy
|
||||
<CasesRoutesLazy
|
||||
onComponentInitialized={onComponentInitialized}
|
||||
actionsNavigation={actionsNavigation}
|
||||
ruleDetailsNavigation={ruleDetailsNavigation}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public';
|
||||
import { CasesUiStart, SetupPlugins, StartPlugins } from './types';
|
||||
import { CasesUiStart, CasesPluginSetup, CasesPluginStart } from './types';
|
||||
import { KibanaServices } from './common/lib/kibana';
|
||||
import {
|
||||
getCasesLazy,
|
||||
|
@ -18,24 +18,74 @@ import {
|
|||
getAllCasesSelectorModalNoProviderLazy,
|
||||
} from './methods';
|
||||
import { CasesUiConfigType } from '../common/ui/types';
|
||||
import { APP_ID, APP_PATH } from '../common/constants';
|
||||
import { APP_TITLE, APP_DESC } from './common/translations';
|
||||
import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public';
|
||||
import { ManagementAppMountParams } from '../../../../src/plugins/management/public';
|
||||
import { Storage } from '../../../../src/plugins/kibana_utils/public';
|
||||
import { getCasesContextLazy } from './methods/get_cases_context';
|
||||
import { useCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout';
|
||||
import { useCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal';
|
||||
import { useCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout';
|
||||
import { getRuleIdFromEvent } from './methods/get_rule_id_from_event';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A plugin for retrieving Cases UI components
|
||||
*/
|
||||
export class CasesUiPlugin implements Plugin<void, CasesUiStart, SetupPlugins, StartPlugins> {
|
||||
private kibanaVersion: string;
|
||||
export class CasesUiPlugin
|
||||
implements Plugin<void, CasesUiStart, CasesPluginSetup, CasesPluginStart>
|
||||
{
|
||||
private readonly kibanaVersion: string;
|
||||
private readonly storage = new Storage(localStorage);
|
||||
|
||||
constructor(private readonly initializerContext: PluginInitializerContext) {
|
||||
this.kibanaVersion = initializerContext.env.packageInfo.version;
|
||||
}
|
||||
public setup(core: CoreSetup, plugins: SetupPlugins) {}
|
||||
|
||||
public start(core: CoreStart, plugins: StartPlugins): CasesUiStart {
|
||||
public setup(core: CoreSetup, plugins: CasesPluginSetup) {
|
||||
const kibanaVersion = this.kibanaVersion;
|
||||
const storage = this.storage;
|
||||
|
||||
if (plugins.home) {
|
||||
plugins.home.featureCatalogue.register({
|
||||
id: APP_ID,
|
||||
title: APP_TITLE,
|
||||
description: APP_DESC,
|
||||
icon: 'watchesApp',
|
||||
path: APP_PATH,
|
||||
showOnHomePage: false,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
||||
|
||||
plugins.management.sections.section.insightsAndAlerting.registerApp({
|
||||
id: APP_ID,
|
||||
title: APP_TITLE,
|
||||
order: 0,
|
||||
async mount(params: ManagementAppMountParams) {
|
||||
const [coreStart, pluginsStart] = (await core.getStartServices()) as [
|
||||
CoreStart,
|
||||
CasesPluginStart,
|
||||
unknown
|
||||
];
|
||||
|
||||
const { renderApp } = await import('./application');
|
||||
|
||||
return renderApp({
|
||||
mountParams: params,
|
||||
coreStart,
|
||||
pluginsStart,
|
||||
storage,
|
||||
kibanaVersion,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Return methods that should be available to other plugins
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: CasesPluginStart): CasesUiStart {
|
||||
const config = this.initializerContext.config.get<CasesUiConfigType>();
|
||||
KibanaServices.init({ ...core, ...plugins, kibanaVersion: this.kibanaVersion, config });
|
||||
return {
|
||||
|
|
|
@ -10,6 +10,12 @@ import { ReactElement, ReactNode } from 'react';
|
|||
import type { DataPublicPluginStart } from '../../../../src/plugins/data/public';
|
||||
import type { EmbeddableStart } from '../../../../src/plugins/embeddable/public';
|
||||
import type { Storage } from '../../../../src/plugins/kibana_utils/public';
|
||||
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
|
||||
import {
|
||||
ManagementSetup,
|
||||
ManagementAppMountParams,
|
||||
} from '../../../../src/plugins/management/public';
|
||||
import { FeaturesPluginStart } from '../..//features/public';
|
||||
import type { LensPublicStart } from '../../lens/public';
|
||||
import type { SecurityPluginSetup } from '../../security/public';
|
||||
import type { SpacesPluginStart } from '../../spaces/public';
|
||||
|
@ -18,6 +24,7 @@ import { CommentRequestAlertType, CommentRequestUserType } from '../common/api';
|
|||
import { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal';
|
||||
import { CreateCaseFlyoutProps } from './components/create/flyout';
|
||||
import { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout';
|
||||
|
||||
import type {
|
||||
CasesOwners,
|
||||
GetAllCasesSelectorModalProps,
|
||||
|
@ -28,16 +35,19 @@ import type {
|
|||
import { GetCasesContextProps } from './methods/get_cases_context';
|
||||
import { getRuleIdFromEvent } from './methods/get_rule_id_from_event';
|
||||
|
||||
export interface SetupPlugins {
|
||||
export interface CasesPluginSetup {
|
||||
security: SecurityPluginSetup;
|
||||
management: ManagementSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
}
|
||||
|
||||
export interface StartPlugins {
|
||||
export interface CasesPluginStart {
|
||||
data: DataPublicPluginStart;
|
||||
embeddable: EmbeddableStart;
|
||||
lens: LensPublicStart;
|
||||
storage: Storage;
|
||||
triggersActionsUi: TriggersActionsStart;
|
||||
features: FeaturesPluginStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
}
|
||||
|
||||
|
@ -48,10 +58,18 @@ export interface StartPlugins {
|
|||
*/
|
||||
|
||||
export type StartServices = CoreStart &
|
||||
StartPlugins & {
|
||||
CasesPluginStart & {
|
||||
security: SecurityPluginSetup;
|
||||
};
|
||||
|
||||
export interface RenderAppProps {
|
||||
mountParams: ManagementAppMountParams;
|
||||
coreStart: CoreStart;
|
||||
pluginsStart: CasesPluginStart;
|
||||
storage: Storage;
|
||||
kibanaVersion: string;
|
||||
}
|
||||
|
||||
export interface CasesUiStart {
|
||||
/**
|
||||
* Returns an object denoting the current user's ability to read and crud cases.
|
||||
|
|
63
x-pack/plugins/cases/server/features.ts
Normal file
63
x-pack/plugins/cases/server/features.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
|
||||
import { KibanaFeatureConfig } from '../../features/common';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server';
|
||||
|
||||
import { APP_ID, FEATURE_ID } from '../common/constants';
|
||||
|
||||
/**
|
||||
* The order of appearance in the feature privilege page
|
||||
* under the management section. Cases should be under
|
||||
* the Actions and Connectors feature
|
||||
*/
|
||||
|
||||
const FEATURE_ORDER = 3100;
|
||||
|
||||
export const getCasesKibanaFeature = (): KibanaFeatureConfig => ({
|
||||
id: FEATURE_ID,
|
||||
name: i18n.translate('xpack.cases.features.casesFeatureName', {
|
||||
defaultMessage: 'Cases',
|
||||
}),
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
app: [],
|
||||
order: FEATURE_ORDER,
|
||||
management: {
|
||||
insightsAndAlerting: [APP_ID],
|
||||
},
|
||||
cases: [APP_ID],
|
||||
privileges: {
|
||||
all: {
|
||||
cases: {
|
||||
all: [APP_ID],
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['crud_cases', 'read_cases'],
|
||||
},
|
||||
read: {
|
||||
cases: {
|
||||
read: [APP_ID],
|
||||
},
|
||||
management: {
|
||||
insightsAndAlerting: [APP_ID],
|
||||
},
|
||||
savedObject: {
|
||||
all: [],
|
||||
read: [],
|
||||
},
|
||||
ui: ['read_cases'],
|
||||
},
|
||||
},
|
||||
});
|
|
@ -28,14 +28,19 @@ import { CasesClient } from './client';
|
|||
import type { CasesRequestHandlerContext } from './types';
|
||||
import { CasesClientFactory } from './client/factory';
|
||||
import { SpacesPluginStart } from '../../spaces/server';
|
||||
import { PluginStartContract as FeaturesPluginStart } from '../../features/server';
|
||||
import {
|
||||
PluginStartContract as FeaturesPluginStart,
|
||||
PluginSetupContract as FeaturesPluginSetup,
|
||||
} from '../../features/server';
|
||||
import { LensServerPluginSetup } from '../../lens/server';
|
||||
import { getCasesKibanaFeature } from './features';
|
||||
import { registerRoutes } from './routes/api/register_routes';
|
||||
import { getExternalRoutes } from './routes/api/get_external_routes';
|
||||
|
||||
export interface PluginsSetup {
|
||||
actions: ActionsPluginSetup;
|
||||
lens: LensServerPluginSetup;
|
||||
features: FeaturesPluginSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
security?: SecurityPluginSetup;
|
||||
}
|
||||
|
@ -77,6 +82,8 @@ export class CasePlugin {
|
|||
this.securityPluginSetup = plugins.security;
|
||||
this.lensEmbeddableFactory = plugins.lens.lensEmbeddableFactory;
|
||||
|
||||
plugins.features.registerKibanaFeature(getCasesKibanaFeature());
|
||||
|
||||
core.savedObjects.registerType(
|
||||
createCaseCommentSavedObjectType({
|
||||
migrationDeps: {
|
||||
|
|
|
@ -111,6 +111,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'apm',
|
||||
'stackAlerts',
|
||||
'canvas',
|
||||
'generalCases',
|
||||
'infrastructure',
|
||||
'logs',
|
||||
'maps',
|
||||
|
|
|
@ -28,6 +28,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
canvas: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
maps: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
generalCases: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
fleetv2: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
fleet: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
|
|
|
@ -30,6 +30,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
savedObjectsTagging: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
graph: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
maps: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
generalCases: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
observabilityCases: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
canvas: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
infrastructure: ['all', 'read', 'minimal_all', 'minimal_read'],
|
||||
|
|
|
@ -64,7 +64,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
expect(sections).to.have.length(2);
|
||||
expect(sections[0]).to.eql({
|
||||
sectionId: 'insightsAndAlerting',
|
||||
sectionLinks: ['triggersActions', 'jobsListLink'],
|
||||
sectionLinks: ['triggersActions', 'cases', 'jobsListLink'],
|
||||
});
|
||||
expect(sections[1]).to.eql({
|
||||
sectionId: 'kibana',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue