Skip to content

Commit

Permalink
disable search sessions in mgtm in case not a single app is using them
Browse files Browse the repository at this point in the history
  • Loading branch information
Dosant committed Feb 9, 2021
1 parent d43547a commit 8685270
Show file tree
Hide file tree
Showing 17 changed files with 175 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) &gt; [SEARCH\_SESSIONS\_MANAGEMENT\_ID](./kibana-plugin-plugins-data-public.search_sessions_management_id.md)

## SEARCH\_SESSIONS\_MANAGEMENT\_ID variable

<b>Signature:</b>

```typescript
SEARCH_SESSIONS_MANAGEMENT_ID = "search_sessions"
```
1 change: 1 addition & 0 deletions src/plugins/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ export {
TimeoutErrorMode,
PainlessError,
noSearchSessionStorageCapabilityMessage,
SEARCH_SESSIONS_MANAGEMENT_ID,
} from './search';

export type {
Expand Down
37 changes: 21 additions & 16 deletions src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/public/search/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export {
SessionsClient,
ISessionsClient,
noSearchSessionStorageCapabilityMessage,
SEARCH_SESSIONS_MANAGEMENT_ID,
} from './session';
export { getEsPreference } from './es_search';

Expand Down
9 changes: 9 additions & 0 deletions src/plugins/data/public/search/session/constants.ts
Original file line number Diff line number Diff line change
@@ -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';
1 change: 1 addition & 0 deletions src/plugins/data/public/search/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
1 change: 1 addition & 0 deletions src/plugins/data/public/search/session/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ export function getSessionServiceMock(): jest.Mocked<ISessionService> {
enableStorage: jest.fn(),
isSessionStorageReady: jest.fn(() => true),
getSearchSessionIndicatorUiConfig: jest.fn(() => ({ isDisabled: () => ({ disabled: false }) })),
hasAccess: jest.fn(() => true),
};
}
14 changes: 13 additions & 1 deletion src/plugins/data/public/search/session/session_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
]),
Expand Down
26 changes: 25 additions & 1 deletion src/plugins/data/public/search/session/session_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SessionService>;

Expand Down Expand Up @@ -68,6 +69,7 @@ export class SessionService {
private searchSessionIndicatorUiConfig?: Partial<SearchSessionIndicatorUiConfig>;
private subscription = new Subscription();
private curApp?: string;
private hasAccessToSearchSessions: boolean = false;

constructor(
initializerContext: PluginInitializerContext<ConfigSchema>,
Expand All @@ -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(
Expand All @@ -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
*
Expand Down Expand Up @@ -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<Pick<ISearchOptions, 'sessionId' | 'isRestore' | 'isStored'>> {
): Required<Pick<ISearchOptions, 'sessionId' | 'isRestore' | 'isStored'>> | 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -108,8 +110,8 @@ export function getTimelionRequestHandler({
interval: visParams.interval,
timezone,
},
...(searchSessionId && {
searchSession: dataSearch.session.getSearchOptions(searchSessionId),
...(searchSessionOptions && {
searchSession: searchSessionOptions,
}),
}),
});
Expand Down
6 changes: 4 additions & 2 deletions src/plugins/vis_type_timeseries/public/request_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -58,8 +60,8 @@ export const metricsRequestHandler = async ({
filters: input?.filters,
panels: [visParams],
state: uiStateObj,
...(searchSessionId && {
searchSession: dataSearch.session.getSearchOptions(searchSessionId),
...(searchSessionOptions && {
searchSession: searchSessionOptions,
}),
}),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ISessionService>;
let storage: Storage;
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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(
<Container>
<SearchSessionIndicator />
</Container>
);

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,
Expand Down Expand Up @@ -199,7 +218,7 @@ describe('Completed inactivity', () => {

const SearchSessionIndicator = createConnectedSearchSessionIndicator({
sessionService: { ...sessionService, state$ },
application: coreStart.application,
application,
timeFilter,
storage,
disableSaveAfterSessionCompletesTimeout,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 8685270

Please sign in to comment.