[Search Session] Fix integration in vega, timelion and TSVB (#87862)
This commit is contained in:
parent
3d749ad444
commit
febe1f5900
|
@ -2624,7 +2624,7 @@ export const UI_SETTINGS: {
|
|||
// src/plugins/data/public/index.ts:433:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
|
||||
// src/plugins/data/public/index.ts:436: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:45: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:51: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:52:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
|
|
|
@ -115,12 +115,14 @@ describe('SearchInterceptor', () => {
|
|||
}: {
|
||||
isRestore?: boolean;
|
||||
isStored?: boolean;
|
||||
sessionId?: string;
|
||||
sessionId: string;
|
||||
}) => {
|
||||
const sessionServiceMock = searchMock.session as jest.Mocked<ISessionService>;
|
||||
sessionServiceMock.getSessionId.mockImplementation(() => sessionId);
|
||||
sessionServiceMock.isRestore.mockImplementation(() => isRestore);
|
||||
sessionServiceMock.isStored.mockImplementation(() => isStored);
|
||||
sessionServiceMock.getSearchOptions.mockImplementation(() => ({
|
||||
sessionId,
|
||||
isRestore,
|
||||
isStored,
|
||||
}));
|
||||
fetchMock.mockResolvedValue({ result: 200 });
|
||||
};
|
||||
|
||||
|
@ -130,30 +132,14 @@ describe('SearchInterceptor', () => {
|
|||
|
||||
afterEach(() => {
|
||||
const sessionServiceMock = searchMock.session as jest.Mocked<ISessionService>;
|
||||
sessionServiceMock.getSessionId.mockReset();
|
||||
sessionServiceMock.isRestore.mockReset();
|
||||
sessionServiceMock.isStored.mockReset();
|
||||
sessionServiceMock.getSearchOptions.mockReset();
|
||||
fetchMock.mockReset();
|
||||
});
|
||||
|
||||
test('infers isRestore from session service state', async () => {
|
||||
test('gets session search options from session service', async () => {
|
||||
const sessionId = 'sid';
|
||||
setup({
|
||||
isRestore: true,
|
||||
sessionId,
|
||||
});
|
||||
|
||||
await searchInterceptor.search(mockRequest, { sessionId }).toPromise();
|
||||
expect(fetchMock.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
options: { sessionId: 'sid', isStored: false, isRestore: true },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('infers isStored from session service state', async () => {
|
||||
const sessionId = 'sid';
|
||||
setup({
|
||||
isStored: true,
|
||||
sessionId,
|
||||
});
|
||||
|
@ -161,41 +147,13 @@ describe('SearchInterceptor', () => {
|
|||
await searchInterceptor.search(mockRequest, { sessionId }).toPromise();
|
||||
expect(fetchMock.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
options: { sessionId: 'sid', isStored: true, isRestore: false },
|
||||
options: { sessionId, isStored: true, isRestore: true },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('skips isRestore & isStore in case not a current session Id', async () => {
|
||||
setup({
|
||||
isStored: true,
|
||||
isRestore: true,
|
||||
sessionId: 'session id',
|
||||
});
|
||||
|
||||
await searchInterceptor
|
||||
.search(mockRequest, { sessionId: 'different session id' })
|
||||
.toPromise();
|
||||
expect(fetchMock.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
options: { sessionId: 'different session id', isStored: false, isRestore: false },
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('skips isRestore & isStore in case no session Id', async () => {
|
||||
setup({
|
||||
isStored: true,
|
||||
isRestore: true,
|
||||
sessionId: undefined,
|
||||
});
|
||||
|
||||
await searchInterceptor.search(mockRequest, { sessionId: 'sessionId' }).toPromise();
|
||||
expect(fetchMock.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
options: { sessionId: 'sessionId', isStored: false, isRestore: false },
|
||||
})
|
||||
);
|
||||
expect(
|
||||
(searchMock.session as jest.Mocked<ISessionService>).getSearchOptions
|
||||
).toHaveBeenCalledWith(sessionId);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -130,16 +130,12 @@ export class SearchInterceptor {
|
|||
): Promise<IKibanaSearchResponse> {
|
||||
const { abortSignal, ...requestOptions } = options || {};
|
||||
|
||||
const isCurrentSession =
|
||||
options?.sessionId && this.deps.session.getSessionId() === options.sessionId;
|
||||
|
||||
return this.batchedFetch(
|
||||
{
|
||||
request,
|
||||
options: {
|
||||
...requestOptions,
|
||||
isStored: isCurrentSession ? this.deps.session.isStored() : false,
|
||||
isRestore: isCurrentSession ? this.deps.session.isRestore() : false,
|
||||
...(options?.sessionId && this.deps.session.getSearchOptions(options.sessionId)),
|
||||
},
|
||||
},
|
||||
abortSignal
|
||||
|
|
|
@ -49,5 +49,7 @@ export function getSessionServiceMock(): jest.Mocked<ISessionService> {
|
|||
isStored: jest.fn(),
|
||||
isRestore: jest.fn(),
|
||||
save: jest.fn(),
|
||||
isCurrentSession: jest.fn(),
|
||||
getSearchOptions: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,10 +33,18 @@ describe('Session service', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
const initializerContext = coreMock.createPluginInitializerContext();
|
||||
const startService = coreMock.createSetup().getStartServices;
|
||||
nowProvider = createNowProviderMock();
|
||||
sessionService = new SessionService(
|
||||
initializerContext,
|
||||
coreMock.createSetup().getStartServices,
|
||||
() =>
|
||||
startService().then(([coreStart, ...rest]) => [
|
||||
{
|
||||
...coreStart,
|
||||
application: { ...coreStart.application, currentAppId$: new BehaviorSubject('app') },
|
||||
},
|
||||
...rest,
|
||||
]),
|
||||
getSessionsClientMock(),
|
||||
nowProvider,
|
||||
{ freezeState: false } // needed to use mocks inside state container
|
||||
|
@ -100,4 +108,63 @@ describe('Session service', () => {
|
|||
expect(abort).toBeCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
test('getSearchOptions infers isRestore & isStored from state', async () => {
|
||||
const sessionId = sessionService.start();
|
||||
const someOtherId = 'some-other-id';
|
||||
|
||||
expect(sessionService.getSearchOptions(someOtherId)).toEqual({
|
||||
isStored: false,
|
||||
isRestore: false,
|
||||
sessionId: someOtherId,
|
||||
});
|
||||
expect(sessionService.getSearchOptions(sessionId)).toEqual({
|
||||
isStored: false,
|
||||
isRestore: false,
|
||||
sessionId,
|
||||
});
|
||||
|
||||
sessionService.setSearchSessionInfoProvider({
|
||||
getName: async () => 'Name',
|
||||
getUrlGeneratorData: async () => ({
|
||||
urlGeneratorId: 'id',
|
||||
initialState: {},
|
||||
restoreState: {},
|
||||
}),
|
||||
});
|
||||
await sessionService.save();
|
||||
|
||||
expect(sessionService.getSearchOptions(someOtherId)).toEqual({
|
||||
isStored: false,
|
||||
isRestore: false,
|
||||
sessionId: someOtherId,
|
||||
});
|
||||
expect(sessionService.getSearchOptions(sessionId)).toEqual({
|
||||
isStored: true,
|
||||
isRestore: false,
|
||||
sessionId,
|
||||
});
|
||||
|
||||
await sessionService.restore(sessionId);
|
||||
|
||||
expect(sessionService.getSearchOptions(someOtherId)).toEqual({
|
||||
isStored: false,
|
||||
isRestore: false,
|
||||
sessionId: someOtherId,
|
||||
});
|
||||
expect(sessionService.getSearchOptions(sessionId)).toEqual({
|
||||
isStored: true,
|
||||
isRestore: true,
|
||||
sessionId,
|
||||
});
|
||||
});
|
||||
test('isCurrentSession', () => {
|
||||
expect(sessionService.isCurrentSession()).toBeFalsy();
|
||||
|
||||
const sessionId = sessionService.start();
|
||||
|
||||
expect(sessionService.isCurrentSession()).toBeFalsy();
|
||||
expect(sessionService.isCurrentSession('some-other')).toBeFalsy();
|
||||
expect(sessionService.isCurrentSession(sessionId)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,6 +29,7 @@ import {
|
|||
SessionStateContainer,
|
||||
} from './search_session_state';
|
||||
import { ISessionsClient } from './sessions_client';
|
||||
import { ISearchOptions } from '../../../common';
|
||||
import { NowProviderInternalContract } from '../../now_provider';
|
||||
|
||||
export type ISessionService = PublicContract<SessionService>;
|
||||
|
@ -256,4 +257,27 @@ export class SessionService {
|
|||
this.state.transitions.store();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if passed sessionId is a current sessionId
|
||||
* @param sessionId
|
||||
*/
|
||||
public isCurrentSession(sessionId?: string): boolean {
|
||||
return !!sessionId && this.getSessionId() === sessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers search session options for sessionId using current session state
|
||||
* @param sessionId
|
||||
*/
|
||||
public getSearchOptions(
|
||||
sessionId: string
|
||||
): Required<Pick<ISearchOptions, 'sessionId' | 'isRestore' | 'isStored'>> {
|
||||
const isCurrentSession = this.isCurrentSession(sessionId);
|
||||
return {
|
||||
sessionId,
|
||||
isRestore: isCurrentSession ? this.isRestore() : false,
|
||||
isStored: isCurrentSession ? this.isStored() : false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,12 +73,15 @@ export function getTimelionRequestHandler({
|
|||
filters,
|
||||
query,
|
||||
visParams,
|
||||
searchSessionId,
|
||||
}: {
|
||||
timeRange: TimeRange;
|
||||
filters: Filter[];
|
||||
query: Query;
|
||||
visParams: TimelionVisParams;
|
||||
searchSessionId?: string;
|
||||
}): Promise<TimelionSuccessResponse> {
|
||||
const dataSearch = getDataSearch();
|
||||
const expression = visParams.expression;
|
||||
|
||||
if (!expression) {
|
||||
|
@ -93,7 +96,13 @@ export function getTimelionRequestHandler({
|
|||
|
||||
// parse the time range client side to make sure it behaves like other charts
|
||||
const timeRangeBounds = timefilter.calculateBounds(timeRange);
|
||||
const sessionId = getDataSearch().session.getSessionId();
|
||||
const untrackSearch =
|
||||
dataSearch.session.isCurrentSession(searchSessionId) &&
|
||||
dataSearch.session.trackSearch({
|
||||
abort: () => {
|
||||
// TODO: support search cancellations
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
return await http.post('/api/timelion/run', {
|
||||
|
@ -110,7 +119,9 @@ export function getTimelionRequestHandler({
|
|||
interval: visParams.interval,
|
||||
timezone,
|
||||
},
|
||||
sessionId,
|
||||
...(searchSessionId && {
|
||||
searchSession: dataSearch.session.getSearchOptions(searchSessionId),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
} catch (e) {
|
||||
|
@ -125,6 +136,11 @@ export function getTimelionRequestHandler({
|
|||
} else {
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
if (untrackSearch && dataSearch.session.isCurrentSession(searchSessionId)) {
|
||||
// call `untrack` if this search still belongs to current session
|
||||
untrackSearch();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ export const getTimelionVisualizationConfig = (
|
|||
help: '',
|
||||
},
|
||||
},
|
||||
async fn(input, args) {
|
||||
async fn(input, args, { getSearchSessionId }) {
|
||||
const timelionRequestHandler = getTimelionRequestHandler(dependencies);
|
||||
|
||||
const visParams = { expression: args.expression, interval: args.interval };
|
||||
|
@ -80,6 +80,7 @@ export const getTimelionVisualizationConfig = (
|
|||
query: get(input, 'query') as Query,
|
||||
filters: get(input, 'filters') as Filter[],
|
||||
visParams,
|
||||
searchSessionId: getSearchSessionId(),
|
||||
});
|
||||
|
||||
response.visType = TIMELION_VIS_NAME;
|
||||
|
|
|
@ -75,7 +75,13 @@ export function runRoute(
|
|||
to: schema.maybe(schema.string()),
|
||||
})
|
||||
),
|
||||
sessionId: schema.maybe(schema.string()),
|
||||
searchSession: schema.maybe(
|
||||
schema.object({
|
||||
sessionId: schema.string(),
|
||||
isRestore: schema.boolean({ defaultValue: false }),
|
||||
isStored: schema.boolean({ defaultValue: false }),
|
||||
})
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -60,19 +60,26 @@ describe('es', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should call data search with sessionId', async () => {
|
||||
test('should call data search with sessionId, isRestore and isStored', async () => {
|
||||
tlConfig = {
|
||||
...stubRequestAndServer({ rawResponse: esResponse }),
|
||||
request: {
|
||||
body: {
|
||||
sessionId: 1,
|
||||
searchSession: {
|
||||
sessionId: '1',
|
||||
isRestore: true,
|
||||
isStored: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await invoke(es, [5], tlConfig);
|
||||
|
||||
expect(tlConfig.context.search.search.mock.calls[0][1]).toHaveProperty('sessionId', 1);
|
||||
const res = tlConfig.context.search.search.mock.calls[0][1];
|
||||
expect(res).toHaveProperty('sessionId', '1');
|
||||
expect(res).toHaveProperty('isRestore', true);
|
||||
expect(res).toHaveProperty('isStored', false);
|
||||
});
|
||||
|
||||
test('returns a seriesList', () => {
|
||||
|
|
|
@ -133,7 +133,7 @@ export default new Datasource('es', {
|
|||
.search(
|
||||
body,
|
||||
{
|
||||
sessionId: tlConfig.request?.body.sessionId,
|
||||
...tlConfig.request?.body.searchSession,
|
||||
},
|
||||
tlConfig.context
|
||||
)
|
||||
|
|
|
@ -284,5 +284,12 @@ export const visPayloadSchema = schema.object({
|
|||
min: stringRequired,
|
||||
max: stringRequired,
|
||||
}),
|
||||
sessionId: schema.maybe(schema.string()),
|
||||
|
||||
searchSession: schema.maybe(
|
||||
schema.object({
|
||||
sessionId: schema.string(),
|
||||
isRestore: schema.boolean({ defaultValue: false }),
|
||||
isStored: schema.boolean({ defaultValue: false }),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
|
|
@ -65,7 +65,7 @@ export const createMetricsFn = (): TimeseriesExpressionFunctionDefinition => ({
|
|||
help: '',
|
||||
},
|
||||
},
|
||||
async fn(input, args) {
|
||||
async fn(input, args, { getSearchSessionId }) {
|
||||
const visParams: TimeseriesVisParams = JSON.parse(args.params);
|
||||
const uiState = JSON.parse(args.uiState);
|
||||
|
||||
|
@ -73,6 +73,7 @@ export const createMetricsFn = (): TimeseriesExpressionFunctionDefinition => ({
|
|||
input,
|
||||
visParams,
|
||||
uiState,
|
||||
searchSessionId: getSearchSessionId(),
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -29,25 +29,37 @@ interface MetricsRequestHandlerParams {
|
|||
input: KibanaContext | null;
|
||||
uiState: Record<string, any>;
|
||||
visParams: TimeseriesVisParams;
|
||||
searchSessionId?: string;
|
||||
}
|
||||
|
||||
export const metricsRequestHandler = async ({
|
||||
input,
|
||||
uiState,
|
||||
visParams,
|
||||
searchSessionId,
|
||||
}: MetricsRequestHandlerParams): Promise<TimeseriesVisData | {}> => {
|
||||
const config = getUISettings();
|
||||
const timezone = getTimezone(config);
|
||||
const uiStateObj = uiState[visParams.type] ?? {};
|
||||
const dataSearch = getDataStart();
|
||||
const parsedTimeRange = dataSearch.query.timefilter.timefilter.calculateBounds(input?.timeRange!);
|
||||
const data = getDataStart();
|
||||
const dataSearch = getDataStart().search;
|
||||
const parsedTimeRange = data.query.timefilter.timefilter.calculateBounds(input?.timeRange!);
|
||||
|
||||
if (visParams && visParams.id && !visParams.isModelInvalid) {
|
||||
const maxBuckets = config.get(MAX_BUCKETS_SETTING);
|
||||
|
||||
validateInterval(parsedTimeRange, visParams, maxBuckets);
|
||||
|
||||
const resp = await getCoreStart().http.post(ROUTES.VIS_DATA, {
|
||||
const untrackSearch =
|
||||
dataSearch.session.isCurrentSession(searchSessionId) &&
|
||||
dataSearch.session.trackSearch({
|
||||
abort: () => {
|
||||
// TODO: support search cancellations
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
return await getCoreStart().http.post(ROUTES.VIS_DATA, {
|
||||
body: JSON.stringify({
|
||||
timerange: {
|
||||
timezone,
|
||||
|
@ -57,11 +69,17 @@ export const metricsRequestHandler = async ({
|
|||
filters: input?.filters,
|
||||
panels: [visParams],
|
||||
state: uiStateObj,
|
||||
sessionId: dataSearch.search.session.getSessionId(),
|
||||
...(searchSessionId && {
|
||||
searchSession: dataSearch.session.getSearchOptions(searchSessionId),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
return resp;
|
||||
} finally {
|
||||
if (untrackSearch && dataSearch.session.isCurrentSession(searchSessionId)) {
|
||||
// untrack if this search still belongs to current session
|
||||
untrackSearch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
@ -66,7 +66,11 @@ describe('AbstractSearchStrategy', () => {
|
|||
const responses = await abstractSearchStrategy.search(
|
||||
{
|
||||
payload: {
|
||||
sessionId: 1,
|
||||
searchSession: {
|
||||
sessionId: '1',
|
||||
isRestore: false,
|
||||
isStored: true,
|
||||
},
|
||||
},
|
||||
requestContext: {
|
||||
search: { search: searchFn },
|
||||
|
@ -85,7 +89,9 @@ describe('AbstractSearchStrategy', () => {
|
|||
indexType: undefined,
|
||||
},
|
||||
{
|
||||
sessionId: 1,
|
||||
sessionId: '1',
|
||||
isRestore: false,
|
||||
isStored: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -66,7 +66,6 @@ const toSanitizedFieldType = (fields: IFieldType[]) => {
|
|||
export abstract class AbstractSearchStrategy {
|
||||
async search(req: ReqFacade<VisPayload>, bodies: any[], indexType?: string) {
|
||||
const requests: any[] = [];
|
||||
const { sessionId } = req.payload;
|
||||
|
||||
bodies.forEach((body) => {
|
||||
requests.push(
|
||||
|
@ -78,9 +77,7 @@ export abstract class AbstractSearchStrategy {
|
|||
...body,
|
||||
},
|
||||
},
|
||||
{
|
||||
sessionId,
|
||||
}
|
||||
req.payload.searchSession
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
|
|
|
@ -40,7 +40,8 @@ export class SearchAPI {
|
|||
constructor(
|
||||
private readonly dependencies: SearchAPIDependencies,
|
||||
private readonly abortSignal?: AbortSignal,
|
||||
public readonly inspectorAdapters?: VegaInspectorAdapters
|
||||
public readonly inspectorAdapters?: VegaInspectorAdapters,
|
||||
private readonly searchSessionId?: string
|
||||
) {}
|
||||
|
||||
search(searchRequests: SearchRequest[]) {
|
||||
|
@ -60,10 +61,7 @@ export class SearchAPI {
|
|||
}
|
||||
|
||||
return search
|
||||
.search(
|
||||
{ params },
|
||||
{ abortSignal: this.abortSignal, sessionId: search.session.getSessionId() }
|
||||
)
|
||||
.search({ params }, { abortSignal: this.abortSignal, sessionId: this.searchSessionId })
|
||||
.pipe(
|
||||
tap((data) => this.inspectSearchResult(data, requestResponders[requestId])),
|
||||
map((data) => ({
|
||||
|
|
|
@ -74,6 +74,7 @@ export const createVegaFn = (
|
|||
query: get(input, 'query') as Query,
|
||||
filters: get(input, 'filters') as any,
|
||||
visParams: { spec: args.spec },
|
||||
searchSessionId: context.getSearchSessionId(),
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -32,6 +32,7 @@ interface VegaRequestHandlerParams {
|
|||
filters: Filter;
|
||||
timeRange: TimeRange;
|
||||
visParams: VisParams;
|
||||
searchSessionId?: string;
|
||||
}
|
||||
|
||||
interface VegaRequestHandlerContext {
|
||||
|
@ -52,6 +53,7 @@ export function createVegaRequestHandler(
|
|||
filters,
|
||||
query,
|
||||
visParams,
|
||||
searchSessionId,
|
||||
}: VegaRequestHandlerParams) {
|
||||
if (!searchAPI) {
|
||||
searchAPI = new SearchAPI(
|
||||
|
@ -61,7 +63,8 @@ export function createVegaRequestHandler(
|
|||
injectedMetadata: getInjectedMetadata(),
|
||||
},
|
||||
context.abortSignal,
|
||||
context.inspectorAdapters
|
||||
context.inspectorAdapters,
|
||||
searchSessionId
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
|
||||
test('should NOT DELETE a running SAVED async search on abort', async () => {
|
||||
const sessionId = 'sessionId';
|
||||
sessionService.getSessionId.mockImplementation(() => sessionId);
|
||||
sessionService.isCurrentSession.mockImplementation((_sessionId) => _sessionId === sessionId);
|
||||
const responses = [
|
||||
{
|
||||
time: 10,
|
||||
|
@ -479,6 +479,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
|
||||
test('should track searches', async () => {
|
||||
const sessionId = 'sessionId';
|
||||
sessionService.isCurrentSession.mockImplementation((_sessionId) => _sessionId === sessionId);
|
||||
sessionService.getSessionId.mockImplementation(() => sessionId);
|
||||
|
||||
const untrack = jest.fn();
|
||||
|
@ -496,6 +497,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
|
||||
test('session service should be able to cancel search', async () => {
|
||||
const sessionId = 'sessionId';
|
||||
sessionService.isCurrentSession.mockImplementation((_sessionId) => _sessionId === sessionId);
|
||||
sessionService.getSessionId.mockImplementation(() => sessionId);
|
||||
|
||||
const untrack = jest.fn();
|
||||
|
@ -519,6 +521,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
|
||||
test("don't track non current session searches", async () => {
|
||||
const sessionId = 'sessionId';
|
||||
sessionService.isCurrentSession.mockImplementation((_sessionId) => _sessionId === sessionId);
|
||||
sessionService.getSessionId.mockImplementation(() => sessionId);
|
||||
|
||||
const untrack = jest.fn();
|
||||
|
@ -539,6 +542,7 @@ describe('EnhancedSearchInterceptor', () => {
|
|||
|
||||
test("don't track if no current session", async () => {
|
||||
sessionService.getSessionId.mockImplementation(() => undefined);
|
||||
sessionService.isCurrentSession.mockImplementation((_sessionId) => false);
|
||||
|
||||
const untrack = jest.fn();
|
||||
sessionService.trackSearch.mockImplementation(() => untrack);
|
||||
|
|
|
@ -64,20 +64,24 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
|
|||
const search = () => this.runSearch({ id, ...request }, searchOptions);
|
||||
|
||||
this.pendingCount$.next(this.pendingCount$.getValue() + 1);
|
||||
const isCurrentSession = () =>
|
||||
!!options.sessionId && options.sessionId === this.deps.session.getSessionId();
|
||||
|
||||
const untrackSearch = isCurrentSession() && this.deps.session.trackSearch({ abort });
|
||||
const untrackSearch =
|
||||
this.deps.session.isCurrentSession(options.sessionId) &&
|
||||
this.deps.session.trackSearch({ abort });
|
||||
|
||||
// track if this search's session will be send to background
|
||||
// if yes, then we don't need to cancel this search when it is aborted
|
||||
let isSavedToBackground = false;
|
||||
const savedToBackgroundSub =
|
||||
isCurrentSession() &&
|
||||
this.deps.session.isCurrentSession(options.sessionId) &&
|
||||
this.deps.session.state$
|
||||
.pipe(
|
||||
skip(1), // ignore any state, we are only interested in transition x -> BackgroundLoading
|
||||
filter((state) => isCurrentSession() && state === SearchSessionState.BackgroundLoading),
|
||||
filter(
|
||||
(state) =>
|
||||
this.deps.session.isCurrentSession(options.sessionId) &&
|
||||
state === SearchSessionState.BackgroundLoading
|
||||
),
|
||||
take(1)
|
||||
)
|
||||
.subscribe(() => {
|
||||
|
@ -93,7 +97,8 @@ export class EnhancedSearchInterceptor extends SearchInterceptor {
|
|||
finalize(() => {
|
||||
this.pendingCount$.next(this.pendingCount$.getValue() - 1);
|
||||
cleanup();
|
||||
if (untrackSearch && isCurrentSession()) {
|
||||
if (untrackSearch && this.deps.session.isCurrentSession(options.sessionId)) {
|
||||
// untrack if this search still belongs to current session
|
||||
untrackSearch();
|
||||
}
|
||||
if (savedToBackgroundSub) {
|
||||
|
|
Loading…
Reference in a new issue