Skip to content

Commit 4886819

Browse files
committed
[data.search] Move search method inside session service and add tests
1 parent 6e80d9f commit 4886819

File tree

3 files changed

+122
-39
lines changed

3 files changed

+122
-39
lines changed

src/plugins/data/server/search/search_service.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
import { BehaviorSubject, from, Observable } from 'rxjs';
20+
import { BehaviorSubject, Observable } from 'rxjs';
2121
import { pick } from 'lodash';
2222
import {
2323
CoreSetup,
@@ -29,7 +29,7 @@ import {
2929
SharedGlobalConfig,
3030
StartServicesAccessor,
3131
} from 'src/core/server';
32-
import { catchError, first, map, switchMap } from 'rxjs/operators';
32+
import { catchError, first, map } from 'rxjs/operators';
3333
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
3434
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
3535
import {
@@ -50,7 +50,11 @@ import { DataPluginStart } from '../plugin';
5050
import { UsageCollectionSetup } from '../../../usage_collection/server';
5151
import { registerUsageCollector } from './collectors/register';
5252
import { usageProvider } from './collectors/usage';
53-
import { BACKGROUND_SESSION_TYPE, searchTelemetry } from '../saved_objects';
53+
import {
54+
BACKGROUND_SESSION_TYPE,
55+
backgroundSessionMapping,
56+
searchTelemetry,
57+
} from '../saved_objects';
5458
import {
5559
IEsSearchRequest,
5660
IEsSearchResponse,
@@ -73,8 +77,6 @@ import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
7377
import { ConfigSchema } from '../../config';
7478
import { BackgroundSessionService, ISearchSessionClient } from './session';
7579
import { registerSessionRoutes } from './routes/session';
76-
import { backgroundSessionMapping } from '../saved_objects';
77-
import { tapFirst } from '../../common/utils';
7880

7981
declare module 'src/core/server' {
8082
interface RequestHandlerContext {
@@ -295,32 +297,17 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
295297
SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest,
296298
SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse
297299
>(
298-
searchRequest: SearchStrategyRequest,
300+
request: SearchStrategyRequest,
299301
options: ISearchOptions,
300302
deps: SearchStrategyDependencies
301303
) => {
302304
const strategy = this.getSearchStrategy<SearchStrategyRequest, SearchStrategyResponse>(
303305
options.strategy
304306
);
305307

306-
// If this is a restored background search session, look up the ID using the provided sessionId
307-
const getSearchRequest = async () =>
308-
!options.isRestore || searchRequest.id
309-
? searchRequest
310-
: {
311-
...searchRequest,
312-
id: await this.sessionService.getId(searchRequest, options, deps),
313-
};
314-
315-
return from(getSearchRequest()).pipe(
316-
switchMap((request) => strategy.search(request, options, deps)),
317-
tapFirst((response) => {
318-
if (searchRequest.id || !options.sessionId || !response.id || options.isRestore) return;
319-
this.sessionService.trackId(searchRequest, response.id, options, {
320-
savedObjectsClient: deps.savedObjectsClient,
321-
});
322-
})
323-
);
308+
return options.sessionId
309+
? this.sessionService.search(strategy, request, options, deps)
310+
: strategy.search(request, options, deps);
324311
};
325312

326313
private cancel = (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => {

src/plugins/data/server/search/session/session_service.test.ts

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
* under the License.
1818
*/
1919

20+
import { of } from 'rxjs';
2021
import type { SavedObject, SavedObjectsClientContract } from 'kibana/server';
22+
import type { SearchStrategyDependencies } from '../types';
2123
import { savedObjectsClientMock } from '../../../../../core/server/mocks';
2224
import { BackgroundSessionStatus } from '../../../common';
2325
import { BACKGROUND_SESSION_TYPE } from '../../saved_objects';
@@ -28,6 +30,7 @@ describe('BackgroundSessionService', () => {
2830
let savedObjectsClient: jest.Mocked<SavedObjectsClientContract>;
2931
let service: BackgroundSessionService;
3032

33+
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
3134
const mockSavedObject: SavedObject = {
3235
id: 'd7170a35-7e2c-48d6-8dec-9a056721b489',
3336
type: BACKGROUND_SESSION_TYPE,
@@ -45,9 +48,13 @@ describe('BackgroundSessionService', () => {
4548
service = new BackgroundSessionService();
4649
});
4750

48-
it('save throws if `name` is not provided', () => {
49-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
51+
it('search throws if `name` is not provided', () => {
52+
expect(() => service.save(sessionId, {}, { savedObjectsClient })).rejects.toMatchInlineSnapshot(
53+
`[Error: Name is required]`
54+
);
55+
});
5056

57+
it('save throws if `name` is not provided', () => {
5158
expect(() => service.save(sessionId, {}, { savedObjectsClient })).rejects.toMatchInlineSnapshot(
5259
`[Error: Name is required]`
5360
);
@@ -56,7 +63,6 @@ describe('BackgroundSessionService', () => {
5663
it('get calls saved objects client', async () => {
5764
savedObjectsClient.get.mockResolvedValue(mockSavedObject);
5865

59-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
6066
const response = await service.get(sessionId, { savedObjectsClient });
6167

6268
expect(response).toBe(mockSavedObject);
@@ -93,7 +99,6 @@ describe('BackgroundSessionService', () => {
9399
};
94100
savedObjectsClient.update.mockResolvedValue(mockUpdateSavedObject);
95101

96-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
97102
const attributes = { name: 'new_name' };
98103
const response = await service.update(sessionId, attributes, { savedObjectsClient });
99104

@@ -108,19 +113,87 @@ describe('BackgroundSessionService', () => {
108113
it('delete calls saved objects client', async () => {
109114
savedObjectsClient.delete.mockResolvedValue({});
110115

111-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
112116
const response = await service.delete(sessionId, { savedObjectsClient });
113117

114118
expect(response).toEqual({});
115119
expect(savedObjectsClient.delete).toHaveBeenCalledWith(BACKGROUND_SESSION_TYPE, sessionId);
116120
});
117121

122+
describe('search', () => {
123+
const mockSearch = jest.fn().mockReturnValue(of({}));
124+
const mockStrategy = { search: mockSearch };
125+
const mockDeps = {} as SearchStrategyDependencies;
126+
127+
beforeEach(() => {
128+
mockSearch.mockClear();
129+
});
130+
131+
it('searches using the original request if not restoring', async () => {
132+
const searchRequest = { params: {} };
133+
const options = { sessionId, isStored: false, isRestore: false };
134+
135+
await service.search(mockStrategy, searchRequest, options, mockDeps).toPromise();
136+
137+
expect(mockSearch).toBeCalledWith(searchRequest, options, mockDeps);
138+
});
139+
140+
it('searches using the original request if `id` is provided', async () => {
141+
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
142+
const searchRequest = { id: searchId, params: {} };
143+
const options = { sessionId, isStored: true, isRestore: true };
144+
145+
await service.search(mockStrategy, searchRequest, options, mockDeps).toPromise();
146+
147+
expect(mockSearch).toBeCalledWith(searchRequest, options, mockDeps);
148+
});
149+
150+
it('searches by looking up an `id` if restoring and `id` is not provided', async () => {
151+
const searchRequest = { params: {} };
152+
const options = { sessionId, isStored: true, isRestore: true };
153+
const spyGetId = jest.spyOn(service, 'getId').mockResolvedValueOnce('my_id');
154+
155+
await service.search(mockStrategy, searchRequest, options, mockDeps).toPromise();
156+
157+
expect(mockSearch).toBeCalledWith({ ...searchRequest, id: 'my_id' }, options, mockDeps);
158+
159+
spyGetId.mockRestore();
160+
});
161+
162+
it('calls `trackId` once if the response contains an `id` and not restoring', async () => {
163+
const searchRequest = { params: {} };
164+
const options = { sessionId, isStored: false, isRestore: false };
165+
const spyTrackId = jest.spyOn(service, 'trackId').mockResolvedValue();
166+
mockSearch.mockReturnValueOnce(of({ id: 'my_id' }, { id: 'my_id' }));
167+
168+
await service.search(mockStrategy, searchRequest, options, mockDeps).toPromise();
169+
170+
expect(spyTrackId).toBeCalledTimes(1);
171+
expect(spyTrackId).toBeCalledWith(searchRequest, 'my_id', options, mockDeps);
172+
173+
spyTrackId.mockRestore();
174+
});
175+
176+
it('does not call `trackId` if restoring', async () => {
177+
const searchRequest = { params: {} };
178+
const options = { sessionId, isStored: true, isRestore: true };
179+
const spyGetId = jest.spyOn(service, 'getId').mockResolvedValueOnce('my_id');
180+
const spyTrackId = jest.spyOn(service, 'trackId').mockResolvedValue();
181+
mockSearch.mockReturnValueOnce(of({ id: 'my_id' }));
182+
183+
await service.search(mockStrategy, searchRequest, options, mockDeps).toPromise();
184+
185+
expect(spyTrackId).not.toBeCalled();
186+
187+
spyGetId.mockRestore();
188+
spyTrackId.mockRestore();
189+
});
190+
});
191+
118192
describe('trackId', () => {
119193
it('stores hash in memory when `isStored` is `false` for when `save` is called', async () => {
120194
const searchRequest = { params: {} };
121195
const requestHash = createRequestHash(searchRequest.params);
122196
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
123-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
124197
const isStored = false;
125198
const name = 'my saved background search session';
126199
const appId = 'my_app_id';
@@ -164,7 +237,6 @@ describe('BackgroundSessionService', () => {
164237
const searchRequest = { params: {} };
165238
const requestHash = createRequestHash(searchRequest.params);
166239
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
167-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
168240
const isStored = true;
169241

170242
await service.trackId(
@@ -191,7 +263,6 @@ describe('BackgroundSessionService', () => {
191263

192264
it('throws if there is not a saved object', () => {
193265
const searchRequest = { params: {} };
194-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
195266

196267
expect(() =>
197268
service.getId(searchRequest, { sessionId, isStored: false }, { savedObjectsClient })
@@ -202,7 +273,6 @@ describe('BackgroundSessionService', () => {
202273

203274
it('throws if not restoring a saved session', () => {
204275
const searchRequest = { params: {} };
205-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
206276

207277
expect(() =>
208278
service.getId(
@@ -219,7 +289,6 @@ describe('BackgroundSessionService', () => {
219289
const searchRequest = { params: {} };
220290
const requestHash = createRequestHash(searchRequest.params);
221291
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
222-
const sessionId = 'd7170a35-7e2c-48d6-8dec-9a056721b489';
223292
const mockSession = {
224293
id: 'd7170a35-7e2c-48d6-8dec-9a056721b489',
225294
type: BACKGROUND_SESSION_TYPE,

src/plugins/data/server/search/session/session_service.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@
1818
*/
1919

2020
import { CoreStart, KibanaRequest, SavedObjectsClientContract } from 'kibana/server';
21+
import { from } from 'rxjs';
22+
import { switchMap } from 'rxjs/operators';
2123
import {
2224
BackgroundSessionSavedObjectAttributes,
25+
BackgroundSessionStatus,
2326
IKibanaSearchRequest,
27+
IKibanaSearchResponse,
2428
ISearchOptions,
2529
SearchSessionFindOptions,
26-
BackgroundSessionStatus,
30+
tapFirst,
2731
} from '../../../common';
2832
import { BACKGROUND_SESSION_TYPE } from '../../saved_objects';
33+
import { ISearchStrategy, SearchStrategyDependencies } from '../types';
2934
import { createRequestHash } from './utils';
3035

3136
const DEFAULT_EXPIRATION = 7 * 24 * 60 * 60 * 1000;
@@ -59,6 +64,32 @@ export class BackgroundSessionService {
5964
this.sessionSearchMap.clear();
6065
};
6166

67+
public search = <Request extends IKibanaSearchRequest, Response extends IKibanaSearchResponse>(
68+
strategy: ISearchStrategy<Request, Response>,
69+
searchRequest: Request,
70+
options: ISearchOptions,
71+
deps: SearchStrategyDependencies
72+
) => {
73+
// If this is a restored background search session, look up the ID using the provided sessionId
74+
const getSearchRequest = async () =>
75+
!options.isRestore || searchRequest.id
76+
? searchRequest
77+
: {
78+
...searchRequest,
79+
id: await this.getId(searchRequest, options, deps),
80+
};
81+
82+
return from(getSearchRequest()).pipe(
83+
switchMap((request) => strategy.search(request, options, deps)),
84+
tapFirst((response) => {
85+
if (searchRequest.id || !options.sessionId || !response.id || options.isRestore) return;
86+
this.trackId(searchRequest, response.id, options, {
87+
savedObjectsClient: deps.savedObjectsClient,
88+
});
89+
})
90+
);
91+
};
92+
6293
// TODO: Generate the `userId` from the realm type/realm name/username
6394
public save = async (
6495
sessionId: string,
@@ -208,10 +239,6 @@ export class BackgroundSessionService {
208239
update: (sessionId: string, attributes: Partial<BackgroundSessionSavedObjectAttributes>) =>
209240
this.update(sessionId, attributes, deps),
210241
delete: (sessionId: string) => this.delete(sessionId, deps),
211-
trackId: (searchRequest: IKibanaSearchRequest, searchId: string, options: ISearchOptions) =>
212-
this.trackId(searchRequest, searchId, options, deps),
213-
getId: (searchRequest: IKibanaSearchRequest, options: ISearchOptions) =>
214-
this.getId(searchRequest, options, deps),
215242
};
216243
};
217244
};

0 commit comments

Comments
 (0)