Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/plugins/data/public/public.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,7 @@ export interface SearchUsageCollector {
// (undocumented)
trackSessionExtended: () => Promise<void>;
// (undocumented)
trackSessionIndicatorTourDisabled: () => Promise<void>;
trackSessionIndicatorSaveDisabled: () => Promise<void>;
// (undocumented)
trackSessionIndicatorTourLoading: () => Promise<void>;
// (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ export const createUsageCollector = (
METRIC_TYPE.LOADED,
SEARCH_EVENT_TYPE.SESSION_INDICATOR_TOUR_RESTORED
),
trackSessionIndicatorTourDisabled: getCollector(
trackSessionIndicatorSaveDisabled: getCollector(
METRIC_TYPE.LOADED,
SEARCH_EVENT_TYPE.SESSION_INDICATOR_TOUR_DISABLED
SEARCH_EVENT_TYPE.SESSION_INDICATOR_SAVE_DISABLED
),
trackSessionSentToBackground: getCollector(
METRIC_TYPE.CLICK,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/search/collectors/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function createSearchUsageCollectorMock(): jest.Mocked<SearchUsageCollect
trackQueryTimedOut: jest.fn(),
trackSessionIndicatorTourLoading: jest.fn(),
trackSessionIndicatorTourRestored: jest.fn(),
trackSessionIndicatorTourDisabled: jest.fn(),
trackSessionIndicatorSaveDisabled: jest.fn(),
trackSessionSentToBackground: jest.fn(),
trackSessionSavedResults: jest.fn(),
trackSessionViewRestored: jest.fn(),
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/data/public/search/collectors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export enum SEARCH_EVENT_TYPE {
/**
* The session indicator was disabled because of a completion timeout
*/
SESSION_INDICATOR_TOUR_DISABLED = 'sessionIndicatorTourDisabled',
SESSION_INDICATOR_SAVE_DISABLED = 'sessionIndicatorSaveDisabled',
/**
* The user clicked to continue a session in the background (prior to results completing)
*/
Expand Down Expand Up @@ -75,7 +75,7 @@ export interface SearchUsageCollector {
trackQueryTimedOut: () => Promise<void>;
trackSessionIndicatorTourLoading: () => Promise<void>;
trackSessionIndicatorTourRestored: () => Promise<void>;
trackSessionIndicatorTourDisabled: () => Promise<void>;
trackSessionIndicatorSaveDisabled: () => Promise<void>;
trackSessionSentToBackground: () => Promise<void>;
trackSessionSavedResults: () => Promise<void>;
trackSessionViewRestored: () => Promise<void>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const application = coreStart.application;
const dataStart = dataPluginMock.createStartContract();
const sessionService = dataStart.search.session as jest.Mocked<ISessionService>;
let storage: Storage;
let usageCollector: SearchUsageCollector;
let usageCollector: jest.Mocked<SearchUsageCollector>;

const refreshInterval$ = new BehaviorSubject<RefreshInterval>({ value: 0, pause: true });
const timeFilter = dataStart.query.timefilter.timefilter as jest.Mocked<TimefilterContract>;
Expand Down Expand Up @@ -232,6 +232,7 @@ describe('Completed inactivity', () => {
timeFilter,
storage,
disableSaveAfterSessionCompletesTimeout,
usageCollector,
});

render(
Expand Down Expand Up @@ -263,12 +264,14 @@ describe('Completed inactivity', () => {
});

expect(screen.getByRole('button', { name: 'Save session' })).not.toBeDisabled();
expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(0);

act(() => {
jest.advanceTimersByTime(2.5 * 60 * 1000);
});

expect(screen.getByRole('button', { name: 'Save session' })).toBeDisabled();
expect(usageCollector.trackSessionIndicatorSaveDisabled).toHaveBeenCalledTimes(1);
});
});

Expand Down Expand Up @@ -318,6 +321,9 @@ describe('tour steps', () => {

expect(storage.get(TOUR_RESTORE_STEP_KEY)).toBeFalsy();
expect(storage.get(TOUR_TAKING_TOO_LONG_STEP_KEY)).toBeTruthy();

expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(1);
expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(0);
});

test("doesn't show tour step if state changed before delay", async () => {
Expand Down Expand Up @@ -349,6 +355,9 @@ describe('tour steps', () => {

expect(storage.get(TOUR_RESTORE_STEP_KEY)).toBeFalsy();
expect(storage.get(TOUR_TAKING_TOO_LONG_STEP_KEY)).toBeFalsy();

expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(0);
expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(0);
});
});

Expand All @@ -373,6 +382,10 @@ describe('tour steps', () => {

expect(storage.get(TOUR_RESTORE_STEP_KEY)).toBeTruthy();
expect(storage.get(TOUR_TAKING_TOO_LONG_STEP_KEY)).toBeTruthy();

expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(0);
expect(usageCollector.trackSessionIsRestored).toHaveBeenCalledTimes(1);
expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(1);
});

test("doesn't show tour for irrelevant state", async () => {
Expand All @@ -397,5 +410,8 @@ describe('tour steps', () => {

expect(storage.get(TOUR_RESTORE_STEP_KEY)).toBeFalsy();
expect(storage.get(TOUR_TAKING_TOO_LONG_STEP_KEY)).toBeFalsy();

expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(0);
expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { debounce, distinctUntilChanged, map, mapTo, switchMap, tap } from 'rxjs/operators';
import { merge, of, timer } from 'rxjs';
import useObservable from 'react-use/lib/useObservable';
Expand All @@ -14,8 +14,8 @@ import { SearchSessionIndicator, SearchSessionIndicatorRef } from '../search_ses
import {
ISessionService,
SearchSessionState,
SearchUsageCollector,
TimefilterContract,
SearchUsageCollector,
} from '../../../../../../../src/plugins/data/public';
import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public';
import { ApplicationStart } from '../../../../../../../src/core/public';
Expand Down Expand Up @@ -60,7 +60,7 @@ export const createConnectedSearchSessionIndicator = ({
),
distinctUntilChanged(),
tap((value) => {
if (value) usageCollector?.trackSessionIndicatorTourDisabled();
if (value) usageCollector?.trackSessionIndicatorSaveDisabled();
})
);

Expand Down Expand Up @@ -164,6 +164,12 @@ export const createConnectedSearchSessionIndicator = ({
usageCollector?.trackViewSessionsList();
}, []);

useEffect(() => {
if (state === SearchSessionState.Restored) {
usageCollector?.trackSessionIsRestored();
}
}, [state]);

if (!sessionService.isSessionStorageReady()) return null;
return (
<RedirectAppLinks application={application}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { useCallback, useEffect } from 'react';
import { once } from 'lodash';
import { IStorageWrapper } from '../../../../../../../src/plugins/kibana_utils/public';
import { SearchSessionIndicatorRef } from '../search_session_indicator';
import {
Expand All @@ -32,6 +33,26 @@ export function useSearchSessionTour(
safeSet(storage, TOUR_RESTORE_STEP_KEY);
}, [storage]);

// Makes sure `trackSessionIndicatorTourLoading` is called only once per sessionId
// if to call `usageCollector?.trackSessionIndicatorTourLoading()` directly inside the `useEffect` below
// it might happen that we cause excessive logging
// ESLint: React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.
// eslint-disable-next-line react-hooks/exhaustive-deps
const trackSessionIndicatorTourLoading = useCallback(
once(() => usageCollector?.trackSessionIndicatorTourLoading()),
[usageCollector, state]
);

// Makes sure `trackSessionIndicatorTourRestored` is called only once per sessionId
// if to call `usageCollector?.trackSessionIndicatorTourRestored()` directly inside the `useEffect` below
// it might happen that we cause excessive logging
// ESLint: React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.
// eslint-disable-next-line react-hooks/exhaustive-deps
const trackSessionIndicatorTourRestored = useCallback(
once(() => usageCollector?.trackSessionIndicatorTourRestored()),
[usageCollector, state]
);

useEffect(() => {
if (searchSessionsDisabled) return;
if (!searchSessionIndicatorRef) return;
Expand All @@ -40,16 +61,15 @@ export function useSearchSessionTour(
if (state === SearchSessionState.Loading) {
if (!safeHas(storage, TOUR_TAKING_TOO_LONG_STEP_KEY)) {
timeoutHandle = window.setTimeout(() => {
usageCollector?.trackSessionIndicatorTourLoading();
trackSessionIndicatorTourLoading();
searchSessionIndicatorRef.openPopover();
}, TOUR_TAKING_TOO_LONG_TIMEOUT);
}
}

if (state === SearchSessionState.Restored) {
usageCollector?.trackSessionIsRestored();
if (!safeHas(storage, TOUR_RESTORE_STEP_KEY)) {
usageCollector?.trackSessionIndicatorTourRestored();
trackSessionIndicatorTourRestored();
searchSessionIndicatorRef.openPopover();
}
}
Expand All @@ -65,6 +85,8 @@ export function useSearchSessionTour(
markOpenedDone,
markRestoredDone,
usageCollector,
trackSessionIndicatorTourRestored,
trackSessionIndicatorTourLoading,
]);

return {
Expand Down