mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Reporting/Discover/Search Sessions] Only use relative time filter when generating share data (#112588)
* only use relative time filter when generating share data * Added comment on absolute time filter. Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> * improve discover search session relative time range test * update discover tests and types for passing in data plugin to getSharingData function * updated reporting to pass in data plugin to getSharingData, also updates jest tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com> Co-authored-by: Anton Dosov <anton.dosov@elastic.co>
This commit is contained in:
parent
ce6ed0875a
commit
25bf795099
9 changed files with 88 additions and 55 deletions
|
@ -112,7 +112,7 @@ export const getTopNavLinks = ({
|
|||
const sharingData = await getSharingData(
|
||||
searchSource,
|
||||
state.appStateContainer.getState(),
|
||||
services.uiSettings
|
||||
services
|
||||
);
|
||||
|
||||
services.share.toggleShareContextMenu({
|
||||
|
|
|
@ -7,32 +7,37 @@
|
|||
*/
|
||||
|
||||
import { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import { IndexPattern } from 'src/plugins/data/public';
|
||||
import type { IndexPattern } from 'src/plugins/data/public';
|
||||
import type { DiscoverServices } from '../../../../build_services';
|
||||
import { dataPluginMock } from '../../../../../../data/public/mocks';
|
||||
import { createSearchSourceMock } from '../../../../../../data/common/search/search_source/mocks';
|
||||
import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common';
|
||||
import { indexPatternMock } from '../../../../__mocks__/index_pattern';
|
||||
import { getSharingData, showPublicUrlSwitch } from './get_sharing_data';
|
||||
|
||||
describe('getSharingData', () => {
|
||||
let mockConfig: IUiSettingsClient;
|
||||
let services: DiscoverServices;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig = {
|
||||
get: (key: string) => {
|
||||
if (key === SORT_DEFAULT_ORDER_SETTING) {
|
||||
return 'desc';
|
||||
}
|
||||
if (key === DOC_HIDE_TIME_COLUMN_SETTING) {
|
||||
services = {
|
||||
data: dataPluginMock.createStartContract(),
|
||||
uiSettings: {
|
||||
get: (key: string) => {
|
||||
if (key === SORT_DEFAULT_ORDER_SETTING) {
|
||||
return 'desc';
|
||||
}
|
||||
if (key === DOC_HIDE_TIME_COLUMN_SETTING) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
} as unknown as IUiSettingsClient;
|
||||
} as DiscoverServices;
|
||||
});
|
||||
|
||||
test('returns valid data for sharing', async () => {
|
||||
const searchSourceMock = createSearchSourceMock({ index: indexPatternMock });
|
||||
const result = await getSharingData(searchSourceMock, { columns: [] }, mockConfig);
|
||||
const result = await getSharingData(searchSourceMock, { columns: [] }, services);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"columns": Array [],
|
||||
|
@ -53,7 +58,7 @@ describe('getSharingData', () => {
|
|||
const result = await getSharingData(
|
||||
searchSourceMock,
|
||||
{ columns: ['column_a', 'column_b'] },
|
||||
mockConfig
|
||||
services
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -90,7 +95,7 @@ describe('getSharingData', () => {
|
|||
'cool-field-6',
|
||||
],
|
||||
},
|
||||
mockConfig
|
||||
services
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -116,7 +121,7 @@ describe('getSharingData', () => {
|
|||
});
|
||||
|
||||
test('fields conditionally do not have prepended timeField', async () => {
|
||||
mockConfig = {
|
||||
services.uiSettings = {
|
||||
get: (key: string) => {
|
||||
if (key === DOC_HIDE_TIME_COLUMN_SETTING) {
|
||||
return true;
|
||||
|
@ -141,7 +146,7 @@ describe('getSharingData', () => {
|
|||
'cool-field-6',
|
||||
],
|
||||
},
|
||||
mockConfig
|
||||
services
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { Capabilities, IUiSettingsClient } from 'kibana/public';
|
||||
import { ISearchSource } from '../../../../../../data/common';
|
||||
import type { Capabilities } from 'kibana/public';
|
||||
import type { IUiSettingsClient } from 'src/core/public';
|
||||
import type { DataPublicPluginStart } from 'src/plugins/data/public';
|
||||
import type { ISearchSource } from 'src/plugins/data/common';
|
||||
import { DOC_HIDE_TIME_COLUMN_SETTING, SORT_DEFAULT_ORDER_SETTING } from '../../../../../common';
|
||||
import type { SavedSearch, SortOrder } from '../../../../saved_searches/types';
|
||||
import { getSortForSearchSource } from '../components/doc_table';
|
||||
|
@ -19,8 +21,9 @@ import { AppState } from '../services/discover_state';
|
|||
export async function getSharingData(
|
||||
currentSearchSource: ISearchSource,
|
||||
state: AppState | SavedSearch,
|
||||
config: IUiSettingsClient
|
||||
services: { uiSettings: IUiSettingsClient; data: DataPublicPluginStart }
|
||||
) {
|
||||
const { uiSettings: config, data } = services;
|
||||
const searchSource = currentSearchSource.createCopy();
|
||||
const index = searchSource.getField('index')!;
|
||||
|
||||
|
@ -28,6 +31,8 @@ export async function getSharingData(
|
|||
'sort',
|
||||
getSortForSearchSource(state.sort as SortOrder[], index, config.get(SORT_DEFAULT_ORDER_SETTING))
|
||||
);
|
||||
// When sharing externally we preserve relative time values
|
||||
searchSource.setField('filter', data.query.timefilter.timefilter.createRelativeFilter(index));
|
||||
searchSource.removeField('highlight');
|
||||
searchSource.removeField('highlightAll');
|
||||
searchSource.removeField('aggs');
|
||||
|
|
|
@ -54,10 +54,8 @@ export function updateSearchSource(
|
|||
|
||||
// this is not the default index pattern, it determines that it's not of type rollup
|
||||
if (indexPatternsUtils.isDefault(indexPattern)) {
|
||||
searchSource.setField(
|
||||
'filter',
|
||||
data.query.timefilter.timefilter.createRelativeFilter(indexPattern)
|
||||
);
|
||||
// Set the date range filter fields from timeFilter using the absolute format. Search sessions requires that it be converted from a relative range
|
||||
searchSource.setField('filter', data.query.timefilter.timefilter.createFilter(indexPattern));
|
||||
}
|
||||
|
||||
if (useNewFieldsApi) {
|
||||
|
|
|
@ -11,8 +11,10 @@ import { CoreStart } from 'src/core/public';
|
|||
import type { SearchSource } from 'src/plugins/data/common';
|
||||
import type { SavedSearch } from 'src/plugins/discover/public';
|
||||
import { coreMock } from '../../../../../src/core/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
|
||||
import type { ILicense, LicensingPluginSetup } from '../../../licensing/public';
|
||||
import { ReportingAPIClient } from '../lib/reporting_api_client';
|
||||
import type { ReportingPublicPluginStartDendencies } from '../plugin';
|
||||
import type { ActionContext } from './get_csv_panel_action';
|
||||
import { ReportingCsvPanelAction } from './get_csv_panel_action';
|
||||
|
||||
|
@ -25,8 +27,8 @@ describe('GetCsvReportPanelAction', () => {
|
|||
let context: ActionContext;
|
||||
let mockLicense$: (state?: LicenseResults) => Rx.Observable<ILicense>;
|
||||
let mockSearchSource: SearchSource;
|
||||
let mockStartServicesPayload: [CoreStart, object, unknown];
|
||||
let mockStartServices$: Rx.Subject<typeof mockStartServicesPayload>;
|
||||
let mockStartServicesPayload: [CoreStart, ReportingPublicPluginStartDendencies, unknown];
|
||||
let mockStartServices$: Rx.Observable<typeof mockStartServicesPayload>;
|
||||
|
||||
beforeAll(() => {
|
||||
if (typeof window.URL.revokeObjectURL === 'undefined') {
|
||||
|
@ -48,14 +50,17 @@ describe('GetCsvReportPanelAction', () => {
|
|||
}) as unknown as LicensingPluginSetup['license$'];
|
||||
};
|
||||
|
||||
mockStartServices$ = new Rx.Subject<[CoreStart, object, unknown]>();
|
||||
mockStartServicesPayload = [
|
||||
{
|
||||
...core,
|
||||
application: { capabilities: { dashboard: { downloadCsv: true } } },
|
||||
} as unknown as CoreStart,
|
||||
{},
|
||||
{
|
||||
data: dataPluginMock.createStartContract(),
|
||||
} as ReportingPublicPluginStartDendencies,
|
||||
null,
|
||||
];
|
||||
mockStartServices$ = Rx.from(Promise.resolve(mockStartServicesPayload));
|
||||
|
||||
mockSearchSource = {
|
||||
createCopy: () => mockSearchSource,
|
||||
|
@ -93,7 +98,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
await panel.execute(context);
|
||||
|
||||
|
@ -130,7 +135,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
await panel.execute(context);
|
||||
|
||||
|
@ -153,7 +158,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
await panel.execute(context);
|
||||
|
||||
|
@ -169,7 +174,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
await panel.execute(context);
|
||||
|
||||
|
@ -187,7 +192,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
await panel.execute(context);
|
||||
|
||||
|
@ -204,14 +209,13 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
await licenseMock$.pipe(first()).toPromise();
|
||||
|
||||
expect(await plugin.isCompatible(context)).toEqual(false);
|
||||
});
|
||||
|
||||
it('sets a display and icon type', () => {
|
||||
it('sets a display and icon type', async () => {
|
||||
const panel = new ReportingCsvPanelAction({
|
||||
core,
|
||||
apiClient,
|
||||
|
@ -220,7 +224,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
expect(panel.getIconType()).toMatchInlineSnapshot(`"document"`);
|
||||
expect(panel.getDisplayName()).toMatchInlineSnapshot(`"Download CSV"`);
|
||||
|
@ -228,25 +232,28 @@ describe('GetCsvReportPanelAction', () => {
|
|||
|
||||
describe('Application UI Capabilities', () => {
|
||||
it(`doesn't allow downloads when UI capability is not enabled`, async () => {
|
||||
mockStartServicesPayload = [
|
||||
{ application: { capabilities: {} } } as unknown as CoreStart,
|
||||
{
|
||||
data: dataPluginMock.createStartContract(),
|
||||
} as ReportingPublicPluginStartDendencies,
|
||||
null,
|
||||
];
|
||||
const startServices$ = Rx.from(Promise.resolve(mockStartServicesPayload));
|
||||
const plugin = new ReportingCsvPanelAction({
|
||||
core,
|
||||
apiClient,
|
||||
license$: mockLicense$(),
|
||||
startServices$: mockStartServices$,
|
||||
startServices$,
|
||||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next([
|
||||
{ application: { capabilities: {} } } as unknown as CoreStart,
|
||||
{},
|
||||
null,
|
||||
]);
|
||||
await startServices$.pipe(first()).toPromise();
|
||||
|
||||
expect(await plugin.isCompatible(context)).toEqual(false);
|
||||
});
|
||||
|
||||
it(`allows downloads when license is valid and UI capability is enabled`, async () => {
|
||||
mockStartServices$ = new Rx.Subject();
|
||||
const plugin = new ReportingCsvPanelAction({
|
||||
core,
|
||||
apiClient,
|
||||
|
@ -255,7 +262,7 @@ describe('GetCsvReportPanelAction', () => {
|
|||
usesUiCapabilities: true,
|
||||
});
|
||||
|
||||
mockStartServices$.next(mockStartServicesPayload);
|
||||
await mockStartServices$.pipe(first()).toPromise();
|
||||
|
||||
expect(await plugin.isCompatible(context)).toEqual(true);
|
||||
});
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import * as Rx from 'rxjs';
|
||||
import type { CoreSetup, IUiSettingsClient, NotificationsSetup } from 'src/core/public';
|
||||
import { first } from 'rxjs/operators';
|
||||
import type { CoreSetup, NotificationsSetup } from 'src/core/public';
|
||||
import { CoreStart } from 'src/core/public';
|
||||
import type { ISearchEmbeddable, SavedSearch } from '../../../../../src/plugins/discover/public';
|
||||
import {
|
||||
|
@ -22,6 +23,7 @@ import type { LicensingPluginSetup } from '../../../licensing/public';
|
|||
import { CSV_REPORTING_ACTION } from '../../common/constants';
|
||||
import { checkLicense } from '../lib/license_check';
|
||||
import { ReportingAPIClient } from '../lib/reporting_api_client';
|
||||
import type { ReportingPublicPluginStartDendencies } from '../plugin';
|
||||
|
||||
function isSavedSearchEmbeddable(
|
||||
embeddable: IEmbeddable | ISearchEmbeddable
|
||||
|
@ -36,7 +38,7 @@ export interface ActionContext {
|
|||
interface Params {
|
||||
apiClient: ReportingAPIClient;
|
||||
core: CoreSetup;
|
||||
startServices$: Rx.Observable<[CoreStart, object, unknown]>;
|
||||
startServices$: Rx.Observable<[CoreStart, ReportingPublicPluginStartDendencies, unknown]>;
|
||||
license$: LicensingPluginSetup['license$'];
|
||||
usesUiCapabilities: boolean;
|
||||
}
|
||||
|
@ -47,16 +49,16 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext>
|
|||
public readonly id = CSV_REPORTING_ACTION;
|
||||
private licenseHasDownloadCsv: boolean = false;
|
||||
private capabilityHasDownloadCsv: boolean = false;
|
||||
private uiSettings: IUiSettingsClient;
|
||||
private notifications: NotificationsSetup;
|
||||
private apiClient: ReportingAPIClient;
|
||||
private startServices$: Params['startServices$'];
|
||||
|
||||
constructor({ core, startServices$, license$, usesUiCapabilities, apiClient }: Params) {
|
||||
this.isDownloading = false;
|
||||
|
||||
this.uiSettings = core.uiSettings;
|
||||
this.notifications = core.notifications;
|
||||
this.apiClient = apiClient;
|
||||
this.startServices$ = startServices$;
|
||||
|
||||
license$.subscribe((license) => {
|
||||
const results = license.check('reporting', 'basic');
|
||||
|
@ -65,7 +67,7 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext>
|
|||
});
|
||||
|
||||
if (usesUiCapabilities) {
|
||||
startServices$.subscribe(([{ application }]) => {
|
||||
this.startServices$.subscribe(([{ application }]) => {
|
||||
this.capabilityHasDownloadCsv = application.capabilities.dashboard?.downloadCsv === true;
|
||||
});
|
||||
} else {
|
||||
|
@ -84,11 +86,12 @@ export class ReportingCsvPanelAction implements ActionDefinition<ActionContext>
|
|||
}
|
||||
|
||||
public async getSearchSource(savedSearch: SavedSearch, embeddable: ISearchEmbeddable) {
|
||||
const [{ uiSettings }, { data }] = await this.startServices$.pipe(first()).toPromise();
|
||||
const { getSharingData } = await loadSharingDataHelpers();
|
||||
return await getSharingData(
|
||||
savedSearch.searchSource,
|
||||
savedSearch, // TODO: get unsaved state (using embeddale.searchScope): https://github.com/elastic/kibana/issues/43977
|
||||
this.uiSettings
|
||||
savedSearch, // TODO: get unsaved state (using embeddable.searchScope): https://github.com/elastic/kibana/issues/43977
|
||||
{ uiSettings, data }
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import * as Rx from 'rxjs';
|
||||
import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators';
|
||||
import type { DataPublicPluginStart } from 'src/plugins/data/public';
|
||||
import {
|
||||
CoreSetup,
|
||||
CoreStart,
|
||||
|
@ -77,6 +78,7 @@ export interface ReportingPublicPluginSetupDendencies {
|
|||
|
||||
export interface ReportingPublicPluginStartDendencies {
|
||||
home: HomePublicPluginStart;
|
||||
data: DataPublicPluginStart;
|
||||
management: ManagementStart;
|
||||
licensing: LicensingPluginStart;
|
||||
uiActions: UiActionsStart;
|
||||
|
@ -134,7 +136,10 @@ export class ReportingPublicPlugin
|
|||
return this.contract;
|
||||
}
|
||||
|
||||
public setup(core: CoreSetup, setupDeps: ReportingPublicPluginSetupDendencies) {
|
||||
public setup(
|
||||
core: CoreSetup<ReportingPublicPluginStartDendencies>,
|
||||
setupDeps: ReportingPublicPluginSetupDendencies
|
||||
) {
|
||||
const { getStartServices, uiSettings } = core;
|
||||
const {
|
||||
home,
|
||||
|
|
|
@ -38,6 +38,7 @@ export function SearchSessionsPageProvider({ getService, getPageObjects }: FtrPr
|
|||
mainUrl: $.findTestSubject('sessionManagementNameCol').text(),
|
||||
created: $.findTestSubject('sessionManagementCreatedCol').text(),
|
||||
expires: $.findTestSubject('sessionManagementExpiresCol').text(),
|
||||
searchesCount: Number($.findTestSubject('sessionManagementNumSearchesCol').text()),
|
||||
app: $.findTestSubject('sessionManagementAppIcon').attr('data-test-app-id'),
|
||||
view: async () => {
|
||||
log.debug('management ui: view the session');
|
||||
|
|
|
@ -26,6 +26,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
const searchSessions = getService('searchSessions');
|
||||
const retry = getService('retry');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const toasts = getService('toasts');
|
||||
|
||||
describe('discover async search', () => {
|
||||
before(async () => {
|
||||
|
@ -112,12 +113,20 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
|
||||
// load URL to restore a saved session
|
||||
await PageObjects.searchSessionsManagement.goTo();
|
||||
const searchSessionList = await PageObjects.searchSessionsManagement.getList();
|
||||
const searchSessionListBeforeRestore = await PageObjects.searchSessionsManagement.getList();
|
||||
const searchesCountBeforeRestore = searchSessionListBeforeRestore[0].searchesCount;
|
||||
// navigate to Discover
|
||||
await searchSessionList[0].view();
|
||||
await searchSessionListBeforeRestore[0].view();
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await searchSessions.expectState('restored');
|
||||
expect(await PageObjects.discover.hasNoResults()).to.be(true);
|
||||
expect(await toasts.getToastCount()).to.be(0); // no session restoration related warnings
|
||||
|
||||
await PageObjects.searchSessionsManagement.goTo();
|
||||
const searchSessionListAfterRestore = await PageObjects.searchSessionsManagement.getList();
|
||||
const searchesCountAfterRestore = searchSessionListAfterRestore[0].searchesCount;
|
||||
|
||||
expect(searchesCountBeforeRestore).to.be(searchesCountAfterRestore); // no new searches started during restore
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue