[Search] Session feature controls (#85846)

* Use filter to bulk find

* Update x-pack/plugins/data_enhanced/server/search/session/session_service.ts

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>

* Dashboard in space test

* Add warning on update failure

* fix merge

* Added functional test for sessions in space

* snapshot

* test cleanup

* sub perms

* test snapshots

* Update tests

* test

* code review

* snap

* Added discover test

* Update x-pack/plugins/data_enhanced/public/search/ui/connected_background_session_indicator/connected_background_session_indicator.tsx

Co-authored-by: Anton Dosov <dosantappdev@gmail.com>

Co-authored-by: Lukas Olson <olson.lukas@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Anton Dosov <dosantappdev@gmail.com>
This commit is contained in:
Liza Katz 2021-01-04 20:57:51 +02:00 committed by GitHub
parent 9ed0bbddde
commit 4415e548b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 262 additions and 10 deletions

View file

@ -57,8 +57,35 @@ test('should show indicator in case there is an active search session', async ()
await waitFor(() => getByTestId('backgroundSessionIndicator'));
});
test('should be disabled when permissions are off', async () => {
const state$ = new BehaviorSubject(SessionState.Loading);
coreStart.application.currentAppId$ = new BehaviorSubject('discover');
(coreStart.application.capabilities as any) = {
discover: {
storeSearchSession: false,
},
};
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
sessionService: { ...sessionService, state$ },
application: coreStart.application,
timeFilter,
});
render(<BackgroundSessionIndicator />);
await waitFor(() => screen.getByTestId('backgroundSessionIndicator'));
expect(screen.getByTestId('backgroundSessionIndicator').querySelector('button')).toBeDisabled();
});
test('should be disabled during auto-refresh', async () => {
const state$ = new BehaviorSubject(SessionState.Loading);
coreStart.application.currentAppId$ = new BehaviorSubject('discover');
(coreStart.application.capabilities as any) = {
discover: {
storeSearchSession: true,
},
};
const BackgroundSessionIndicator = createConnectedBackgroundSessionIndicator({
sessionService: { ...sessionService, state$ },
application: coreStart.application,

View file

@ -29,12 +29,35 @@ export const createConnectedBackgroundSessionIndicator = ({
.getRefreshIntervalUpdate$()
.pipe(map(isAutoRefreshEnabled), distinctUntilChanged());
const getCapabilitiesByAppId = (
capabilities: ApplicationStart['capabilities'],
appId?: string
) => {
switch (appId) {
case 'dashboards':
return capabilities.dashboard;
case 'discover':
return capabilities.discover;
default:
return undefined;
}
};
return () => {
const state = useObservable(sessionService.state$.pipe(debounceTime(500)));
const autoRefreshEnabled = useObservable(isAutoRefreshEnabled$, isAutoRefreshEnabled());
const appId = useObservable(application.currentAppId$, undefined);
let disabled = false;
let disabledReasonText: string = '';
if (getCapabilitiesByAppId(application.capabilities, appId)?.storeSearchSession !== true) {
disabled = true;
disabledReasonText = i18n.translate('xpack.data.backgroundSessionIndicator.noCapability', {
defaultMessage: "You don't have permissions to send to background.",
});
}
if (autoRefreshEnabled) {
disabled = true;
disabledReasonText = i18n.translate(

View file

@ -73,6 +73,7 @@ Array [
"dashboard",
"query",
"url",
"background-session",
],
"read": Array [
"index-pattern",
@ -83,7 +84,6 @@ Array [
"lens",
"map",
"tag",
"background-session",
],
},
"ui": Array [
@ -92,6 +92,7 @@ Array [
"showWriteControls",
"saveQuery",
"createShortUrl",
"storeSearchSession",
],
},
"privilegeId": "all",
@ -205,8 +206,8 @@ Array [
"search",
"query",
"index-pattern",
"background-session",
"url",
"background-session",
],
"read": Array [],
},
@ -215,6 +216,7 @@ Array [
"save",
"saveQuery",
"createShortUrl",
"storeSearchSession",
],
},
"privilegeId": "all",
@ -557,6 +559,7 @@ Array [
"dashboard",
"query",
"url",
"background-session",
],
"read": Array [
"index-pattern",
@ -567,7 +570,6 @@ Array [
"lens",
"map",
"tag",
"background-session",
],
},
"ui": Array [
@ -576,6 +578,7 @@ Array [
"showWriteControls",
"saveQuery",
"createShortUrl",
"storeSearchSession",
],
},
"privilegeId": "all",
@ -689,8 +692,8 @@ Array [
"search",
"query",
"index-pattern",
"background-session",
"url",
"background-session",
],
"read": Array [],
},
@ -699,6 +702,7 @@ Array [
"save",
"saveQuery",
"createShortUrl",
"storeSearchSession",
],
},
"privilegeId": "all",

View file

@ -28,7 +28,7 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
app: ['discover', 'kibana'],
catalogue: ['discover'],
savedObject: {
all: ['search', 'query', 'index-pattern', 'background-session'],
all: ['search', 'query', 'index-pattern'],
read: [],
},
ui: ['show', 'save', 'saveQuery'],
@ -71,6 +71,33 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
},
],
},
{
name: i18n.translate('xpack.features.ossFeatures.discoverSearchSessionsFeatureName', {
defaultMessage: 'Store Search Sessions',
}),
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'store_search_session',
name: i18n.translate(
'xpack.features.ossFeatures.discoverStoreSearchSessionsPrivilegeName',
{
defaultMessage: 'Store Search Sessions',
}
),
includeIn: 'all',
savedObject: {
all: ['background-session'],
read: [],
},
ui: ['storeSearchSession'],
},
],
},
],
},
],
},
{
@ -156,7 +183,6 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
'lens',
'map',
'tag',
'background-session',
],
},
ui: ['createNew', 'show', 'showWriteControls', 'saveQuery'],
@ -210,6 +236,33 @@ export const buildOSSFeatures = ({ savedObjectTypes, includeTimelion }: BuildOSS
},
],
},
{
name: i18n.translate('xpack.features.ossFeatures.dashboardSearchSessionsFeatureName', {
defaultMessage: 'Store Search Sessions',
}),
privilegeGroups: [
{
groupType: 'independent',
privileges: [
{
id: 'store_search_session',
name: i18n.translate(
'xpack.features.ossFeatures.dashboardStoreSearchSessionsPrivilegeName',
{
defaultMessage: 'Store Search Sessions',
}
),
includeIn: 'all',
savedObject: {
all: ['background-session'],
read: [],
},
ui: ['storeSearchSession'],
},
],
},
],
},
],
},
{

View file

@ -24,6 +24,7 @@ export default function ({ getService }: FtrProviderContext) {
'minimal_all',
'minimal_read',
'url_create',
'store_search_session',
];
const trialPrivileges = await supertest
.get('/api/security/privileges')

View file

@ -20,9 +20,23 @@ export default function ({ getService }: FtrProviderContext) {
// Roles are associated with these privileges, and we shouldn't be removing them in a minor version.
const expected = {
features: {
discover: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'],
discover: [
'all',
'read',
'minimal_all',
'minimal_read',
'url_create',
'store_search_session',
],
visualize: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'],
dashboard: ['all', 'read', 'minimal_all', 'minimal_read', 'url_create'],
dashboard: [
'all',
'read',
'minimal_all',
'minimal_read',
'url_create',
'store_search_session',
],
dev_tools: ['all', 'read'],
advancedSettings: ['all', 'read'],
indexPatterns: ['all', 'read'],

View file

@ -20,7 +20,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
reportName: 'X-Pack Background Search UI (Enabled WIP Feature)',
},
testFiles: [resolve(__dirname, './tests/apps/dashboard/async_search')],
testFiles: [
resolve(__dirname, './tests/apps/dashboard/async_search'),
resolve(__dirname, './tests/apps/discover'),
],
kbnTestServer: {
...xpackFunctionalConfig.get('kbnTestServer'),

View file

@ -34,7 +34,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
},
kibana: [
{
base: ['all'],
feature: {
dashboard: ['minimal_read', 'store_search_session'],
},
spaces: ['another-space'],
},
],
@ -54,6 +56,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
after(async () => {
await security.role.delete('data_analyst');
await security.user.delete('analyst');
await esArchiver.unload('dashboard/session_in_space');
await PageObjects.security.forceLogout();
});

View file

@ -0,0 +1,22 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile, getService }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
describe('async search', function () {
this.tags('ciGroup3');
before(async () => {
await esArchiver.loadIfNeeded('logstash_functional');
await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' });
});
loadTestFile(require.resolve('./sessions_in_space'));
});
}

View file

@ -0,0 +1,100 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const esArchiver = getService('esArchiver');
const security = getService('security');
const inspector = getService('inspector');
const PageObjects = getPageObjects([
'common',
'header',
'discover',
'visChart',
'security',
'timePicker',
]);
const browser = getService('browser');
const sendToBackground = getService('sendToBackground');
describe('discover in space', () => {
describe('Send to background in space', () => {
before(async () => {
await esArchiver.load('dashboard/session_in_space');
await security.role.create('data_analyst', {
elasticsearch: {
indices: [{ names: ['logstash-*'], privileges: ['all'] }],
},
kibana: [
{
feature: {
discover: ['all'],
},
spaces: ['another-space'],
},
],
});
await security.user.create('analyst', {
password: 'analyst-password',
roles: ['data_analyst'],
full_name: 'test user',
});
await PageObjects.security.forceLogout();
await PageObjects.security.login('analyst', 'analyst-password', {
expectSpaceSelector: false,
});
});
after(async () => {
await security.role.delete('data_analyst');
await security.user.delete('analyst');
await esArchiver.unload('dashboard/session_in_space');
await PageObjects.security.forceLogout();
});
it('Saves and restores a session', async () => {
await PageObjects.common.navigateToApp('discover', { basePath: 's/another-space' });
await PageObjects.discover.selectIndexPattern('logstash-*');
await PageObjects.timePicker.setAbsoluteRange(
'Sep 1, 2015 @ 00:00:00.000',
'Oct 1, 2015 @ 00:00:00.000'
);
await PageObjects.discover.waitForDocTableLoadingComplete();
await sendToBackground.expectState('completed');
await sendToBackground.save();
await sendToBackground.expectState('backgroundCompleted');
await inspector.open();
const savedSessionId = await (
await testSubjects.find('inspectorRequestSearchSessionId')
).getAttribute('data-search-session-id');
await inspector.close();
// load URL to restore a saved session
const url = await browser.getCurrentUrl();
const savedSessionURL = `${url}&searchSessionId=${savedSessionId}`;
await browser.get(savedSessionURL);
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.discover.waitForDocTableLoadingComplete();
// Check that session is restored
await sendToBackground.expectState('restored');
await testSubjects.missingOrFail('embeddableErrorLabel');
});
});
});
}