From 8685270d4d4cc808c1f860a2cc016e77eefae4a4 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Tue, 9 Feb 2021 18:10:45 +0100 Subject: [PATCH] disable search sessions in mgtm in case not a single app is using them --- .../kibana-plugin-plugins-data-public.md | 1 + ...ta-public.search_sessions_management_id.md | 11 ++++ src/plugins/data/public/index.ts | 1 + src/plugins/data/public/public.api.md | 37 ++++++++------ src/plugins/data/public/search/index.ts | 1 + .../data/public/search/session/constants.ts | 9 ++++ .../data/public/search/session/index.ts | 1 + .../data/public/search/session/mocks.ts | 1 + .../search/session/session_service.test.ts | 14 ++++- .../public/search/session/session_service.ts | 26 +++++++++- .../helpers/timelion_request_handler.ts | 6 ++- .../public/request_handler.ts | 6 ++- .../public/search/sessions_mgmt/index.ts | 3 +- ...onnected_search_session_indicator.test.tsx | 51 +++++++++++++------ .../connected_search_session_indicator.tsx | 17 +++++++ .../search_session_indicator.tsx | 28 ++++++---- .../plugins/features/server/oss_features.ts | 12 +++++ 17 files changed, 175 insertions(+), 50 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search_sessions_management_id.md create mode 100644 src/plugins/data/public/search/session/constants.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index f576d795b93a5..d2e7ef9db05e8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -126,6 +126,7 @@ | [noSearchSessionStorageCapabilityMessage](./kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md) | Message to display in case storing session session is disabled due to turned off capability | | [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md) | | | [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | | +| [SEARCH\_SESSIONS\_MANAGEMENT\_ID](./kibana-plugin-plugins-data-public.search_sessions_management_id.md) | | | [search](./kibana-plugin-plugins-data-public.search.md) | | | [SearchBar](./kibana-plugin-plugins-data-public.searchbar.md) | | | [syncQueryStateWithUrl](./kibana-plugin-plugins-data-public.syncquerystatewithurl.md) | Helper to setup syncing of global data with the URL | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search_sessions_management_id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search_sessions_management_id.md new file mode 100644 index 0000000000000..ad16d21403a98 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search_sessions_management_id.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SEARCH\_SESSIONS\_MANAGEMENT\_ID](./kibana-plugin-plugins-data-public.search_sessions_management_id.md) + +## SEARCH\_SESSIONS\_MANAGEMENT\_ID variable + +Signature: + +```typescript +SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions" +``` diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 17533eec0a0fa..83a248ee2c3de 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -381,6 +381,7 @@ export { TimeoutErrorMode, PainlessError, noSearchSessionStorageCapabilityMessage, + SEARCH_SESSIONS_MANAGEMENT_ID, } from './search'; export type { diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 408573e12eba5..95c849ce74248 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2238,6 +2238,11 @@ export const search: { tabifyGetColumns: typeof tabifyGetColumns; }; +// Warning: (ae-missing-release-tag) "SEARCH_SESSIONS_MANAGEMENT_ID" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions"; + // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2601,23 +2606,23 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:396:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:396:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:396:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:396:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:416:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:416:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:417:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/search/session/session_service.ts:41:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/search/session/session_service.ts:42:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 31a94d69ddf02..b1e0bc490823a 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -38,6 +38,7 @@ export { SessionsClient, ISessionsClient, noSearchSessionStorageCapabilityMessage, + SEARCH_SESSIONS_MANAGEMENT_ID, } from './session'; export { getEsPreference } from './es_search'; diff --git a/src/plugins/data/public/search/session/constants.ts b/src/plugins/data/public/search/session/constants.ts new file mode 100644 index 0000000000000..5496a541bfd45 --- /dev/null +++ b/src/plugins/data/public/search/session/constants.ts @@ -0,0 +1,9 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const SEARCH_SESSIONS_MANAGEMENT_ID = 'search_sessions'; diff --git a/src/plugins/data/public/search/session/index.ts b/src/plugins/data/public/search/session/index.ts index 82ba1e703a6d6..15410400a33e6 100644 --- a/src/plugins/data/public/search/session/index.ts +++ b/src/plugins/data/public/search/session/index.ts @@ -10,3 +10,4 @@ export { SessionService, ISessionService, SearchSessionInfoProvider } from './se export { SearchSessionState } from './search_session_state'; export { SessionsClient, ISessionsClient } from './sessions_client'; export { noSearchSessionStorageCapabilityMessage } from './i18n'; +export { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants'; diff --git a/src/plugins/data/public/search/session/mocks.ts b/src/plugins/data/public/search/session/mocks.ts index f6a70d157b5a0..c615be641078b 100644 --- a/src/plugins/data/public/search/session/mocks.ts +++ b/src/plugins/data/public/search/session/mocks.ts @@ -41,5 +41,6 @@ export function getSessionServiceMock(): jest.Mocked { enableStorage: jest.fn(), isSessionStorageReady: jest.fn(() => true), getSearchSessionIndicatorUiConfig: jest.fn(() => ({ isDisabled: () => ({ disabled: false }) })), + hasAccess: jest.fn(() => true), }; } diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index 54c402f51ec70..85c4a00f6f862 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -14,6 +14,7 @@ import { BehaviorSubject } from 'rxjs'; import { SearchSessionState } from './search_session_state'; import { createNowProviderMock } from '../../now_provider/mocks'; import { NowProviderInternalContract } from '../../now_provider'; +import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants'; describe('Session service', () => { let sessionService: ISessionService; @@ -30,7 +31,18 @@ describe('Session service', () => { startService().then(([coreStart, ...rest]) => [ { ...coreStart, - application: { ...coreStart.application, currentAppId$: new BehaviorSubject('app') }, + application: { + ...coreStart.application, + currentAppId$: new BehaviorSubject('app'), + capabilities: { + ...coreStart.application.capabilities, + management: { + kibana: { + [SEARCH_SESSIONS_MANAGEMENT_ID]: true, + }, + }, + }, + }, }, ...rest, ]), diff --git a/src/plugins/data/public/search/session/session_service.ts b/src/plugins/data/public/search/session/session_service.ts index 79ae64c5846a5..97e46fce37e8f 100644 --- a/src/plugins/data/public/search/session/session_service.ts +++ b/src/plugins/data/public/search/session/session_service.ts @@ -20,6 +20,7 @@ import { import { ISessionsClient } from './sessions_client'; import { ISearchOptions } from '../../../common'; import { NowProviderInternalContract } from '../../now_provider'; +import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants'; export type ISessionService = PublicContract; @@ -68,6 +69,7 @@ export class SessionService { private searchSessionIndicatorUiConfig?: Partial; private subscription = new Subscription(); private curApp?: string; + private hasAccessToSearchSessions: boolean = false; constructor( initializerContext: PluginInitializerContext, @@ -94,6 +96,9 @@ export class SessionService { ); getStartServices().then(([coreStart]) => { + this.hasAccessToSearchSessions = + coreStart.application.capabilities.management?.kibana?.[SEARCH_SESSIONS_MANAGEMENT_ID]; + // Apps required to clean up their sessions before unmounting // Make sure that apps don't leave sessions open. this.subscription.add( @@ -117,6 +122,15 @@ export class SessionService { }); } + /** + * If user has access to search sessions + * This resolves to `true` in case at least one app allows user to create search session + * In this case search session management is available + */ + public hasAccess() { + return this.hasAccessToSearchSessions; + } + /** * Used to track pending searches within current session * @@ -247,11 +261,21 @@ export class SessionService { /** * Infers search session options for sessionId using current session state + * + * In case user doesn't has access to `search-session` SO returns null, + * meaning that sessionId and other session parameters shouldn't be used when doing searches + * * @param sessionId */ public getSearchOptions( sessionId: string - ): Required> { + ): Required> | null { + // in case user doesn't have permissions to search session, do not forward sessionId to the server + // because user most likely also doesn't have access to `search-session` SO + if (!this.hasAccessToSearchSessions) { + return null; + } + const isCurrentSession = this.isCurrentSession(sessionId); return { sessionId, diff --git a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts index c07fd0a278197..832a98a4b28b7 100644 --- a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts +++ b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts @@ -94,6 +94,8 @@ export function getTimelionRequestHandler({ }); try { + const searchSessionOptions = + searchSessionId && dataSearch.session.getSearchOptions(searchSessionId); return await http.post('/api/timelion/run', { body: JSON.stringify({ sheet: [expression], @@ -108,8 +110,8 @@ export function getTimelionRequestHandler({ interval: visParams.interval, timezone, }, - ...(searchSessionId && { - searchSession: dataSearch.session.getSearchOptions(searchSessionId), + ...(searchSessionOptions && { + searchSession: searchSessionOptions, }), }), }); diff --git a/src/plugins/vis_type_timeseries/public/request_handler.ts b/src/plugins/vis_type_timeseries/public/request_handler.ts index c7beccbceca1a..df321331e6429 100644 --- a/src/plugins/vis_type_timeseries/public/request_handler.ts +++ b/src/plugins/vis_type_timeseries/public/request_handler.ts @@ -48,6 +48,8 @@ export const metricsRequestHandler = async ({ }); try { + const searchSessionOptions = + searchSessionId && dataSearch.session.getSearchOptions(searchSessionId); return await getCoreStart().http.post(ROUTES.VIS_DATA, { body: JSON.stringify({ timerange: { @@ -58,8 +60,8 @@ export const metricsRequestHandler = async ({ filters: input?.filters, panels: [visParams], state: uiStateObj, - ...(searchSessionId && { - searchSession: dataSearch.session.getSearchOptions(searchSessionId), + ...(searchSessionOptions && { + searchSession: searchSessionOptions, }), }), }); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts index 332b30809077c..e13cd06f52a4d 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts @@ -15,6 +15,7 @@ import type { ConfigSchema } from '../../../config'; import type { DataEnhancedStartDependencies } from '../../plugin'; import type { SearchSessionsMgmtAPI } from './lib/api'; import type { AsyncSearchIntroDocumentation } from './lib/documentation'; +import { SEARCH_SESSIONS_MANAGEMENT_ID } from '../../../../../../src/plugins/data/public'; export interface IManagementSectionsPluginsSetup { management: ManagementSetup; @@ -38,7 +39,7 @@ export interface AppDependencies { } export const APP = { - id: 'search_sessions', + id: SEARCH_SESSIONS_MANAGEMENT_ID, getI18nName: (): string => i18n.translate('xpack.data.mgmt.searchSessions.appTitle', { defaultMessage: 'Search Sessions', diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx index 3437920ed7c98..aacb86f269727 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx @@ -24,6 +24,7 @@ import userEvent from '@testing-library/user-event'; import { IntlProvider } from 'react-intl'; const coreStart = coreMock.createStart(); +const application = coreStart.application; const dataStart = dataPluginMock.createStartContract(); const sessionService = dataStart.search.session as jest.Mocked; let storage: Storage; @@ -52,7 +53,7 @@ beforeEach(() => { test("shouldn't show indicator in case no active search session", async () => { const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -79,7 +80,7 @@ test("shouldn't show indicator in case no active search session", async () => { test("shouldn't show indicator in case app hasn't opt-in", async () => { const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -108,7 +109,7 @@ test('should show indicator in case there is an active search session', async () const state$ = new BehaviorSubject(SearchSessionState.Loading); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -124,12 +125,6 @@ test('should show indicator in case there is an active search session', async () test('should be disabled in case uiConfig says so ', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); - coreStart.application.currentAppId$ = new BehaviorSubject('discover'); - (coreStart.application.capabilities as any) = { - discover: { - storeSearchSession: false, - }, - }; sessionService.getSearchSessionIndicatorUiConfig.mockImplementation(() => ({ isDisabled: () => ({ disabled: true, @@ -138,7 +133,7 @@ test('should be disabled in case uiConfig says so ', async () => { })); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -157,12 +152,36 @@ test('should be disabled in case uiConfig says so ', async () => { expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled(); }); +test('should be disabled in case not enough permissions', async () => { + const state$ = new BehaviorSubject(SearchSessionState.Completed); + const SearchSessionIndicator = createConnectedSearchSessionIndicator({ + sessionService: { ...sessionService, state$, hasAccess: () => false }, + application, + timeFilter, + storage, + disableSaveAfterSessionCompletesTimeout, + }); + + render( + + + + ); + + await waitFor(() => screen.getByTestId('searchSessionIndicator')); + + await userEvent.click(screen.getByLabelText('Search session complete')); + + expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled(); + expect(screen.getByRole('button', { name: 'Manage sessions' })).toBeDisabled(); +}); + test('should be disabled during auto-refresh', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -199,7 +218,7 @@ describe('Completed inactivity', () => { const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -257,7 +276,7 @@ describe('tour steps', () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -294,7 +313,7 @@ describe('tour steps', () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -325,7 +344,7 @@ describe('tour steps', () => { const state$ = new BehaviorSubject(SearchSessionState.Restored); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, @@ -347,7 +366,7 @@ describe('tour steps', () => { const state$ = new BehaviorSubject(SearchSessionState.Completed); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, - application: coreStart.application, + application, timeFilter, storage, disableSaveAfterSessionCompletesTimeout, diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx index 3935b5bb2814b..81769e5a25544 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx @@ -79,6 +79,9 @@ export const createConnectedSearchSessionIndicator = ({ let saveDisabled = false; let saveDisabledReasonText: string = ''; + let managementDisabled = false; + let managementDisabledReasonText: string = ''; + if (autoRefreshEnabled) { saveDisabled = true; saveDisabledReasonText = i18n.translate( @@ -104,6 +107,18 @@ export const createConnectedSearchSessionIndicator = ({ saveDisabledReasonText = isSaveDisabledByApp.reasonText; } + // check if user doesn't have access to search_sessions and search_sessions mgtm + // this happens in case there is no app that allows current user to use search session + if (!sessionService.hasAccess()) { + managementDisabled = saveDisabled = true; + managementDisabledReasonText = saveDisabledReasonText = i18n.translate( + 'xpack.data.searchSessionIndicator.disabledDueToDisabledGloballyMessage', + { + defaultMessage: "You don't have permissions to manage search sessions", + } + ); + } + const { markOpenedDone, markRestoredDone } = useSearchSessionTour( storage, searchSessionIndicator, @@ -143,6 +158,8 @@ export const createConnectedSearchSessionIndicator = ({ state={state} saveDisabled={saveDisabled} saveDisabledReasonText={saveDisabledReasonText} + managementDisabled={managementDisabled} + managementDisabledReasonText={managementDisabledReasonText} onContinueInBackground={onContinueInBackground} onSaveResults={onSaveResults} onCancel={onCancel} diff --git a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx index eb58039ff58f7..0d31ce0c98f19 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx @@ -31,7 +31,8 @@ export interface SearchSessionIndicatorProps { onCancel?: () => void; viewSearchSessionsLink?: string; onSaveResults?: () => void; - + managementDisabled?: boolean; + managementDisabledReasonText?: string; saveDisabled?: boolean; saveDisabledReasonText?: string; @@ -78,17 +79,22 @@ const ContinueInBackgroundButton = ({ const ViewAllSearchSessionsButton = ({ viewSearchSessionsLink = 'management/kibana/search_sessions', buttonProps = {}, + managementDisabled, + managementDisabledReasonText, }: ActionButtonProps) => ( - - - + + + + + ); const SaveButton = ({ diff --git a/x-pack/plugins/features/server/oss_features.ts b/x-pack/plugins/features/server/oss_features.ts index 2d9e01427a277..6c599461f438a 100644 --- a/x-pack/plugins/features/server/oss_features.ts +++ b/x-pack/plugins/features/server/oss_features.ts @@ -21,6 +21,9 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS name: i18n.translate('xpack.features.discoverFeatureName', { defaultMessage: 'Discover', }), + management: { + kibana: ['search_sessions'], + }, order: 100, category: DEFAULT_APP_CATEGORIES.kibana, app: ['discover', 'kibana'], @@ -95,6 +98,9 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS read: [], }, ui: ['storeSearchSession'], + management: { + kibana: ['search_sessions'], + }, }, ], }, @@ -166,6 +172,9 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS name: i18n.translate('xpack.features.dashboardFeatureName', { defaultMessage: 'Dashboard', }), + management: { + kibana: ['search_sessions'], + }, order: 200, category: DEFAULT_APP_CATEGORIES.kibana, app: ['dashboards', 'kibana'], @@ -260,6 +269,9 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS read: [], }, ui: ['storeSearchSession'], + management: { + kibana: ['search_sessions'], + }, }, ], },