* OSS search functional tests * x-pack tests * Session tests * Update search.ts * Update session.ts * Update test/api_integration/apis/search/search.ts Co-authored-by: Lukas Olson <olson.lukas@gmail.com> * reportServerError * Improve response error codes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Lukas Olson <olson.lukas@gmail.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Lukas Olson <olson.lukas@gmail.com>
This commit is contained in:
parent
19e96960b6
commit
485667511e
|
@ -25,6 +25,7 @@ import type { SearchUsage } from '../collectors';
|
|||
import { getDefaultSearchParams, getShardTimeout, shimAbortSignal } from './request_utils';
|
||||
import { toKibanaSearchResponse } from './response_utils';
|
||||
import { searchUsageObserver } from '../collectors/usage';
|
||||
import { KbnServerError } from '../../../../kibana_utils/server';
|
||||
|
||||
export const esSearchStrategyProvider = (
|
||||
config$: Observable<SharedGlobalConfig>,
|
||||
|
@ -35,7 +36,7 @@ export const esSearchStrategyProvider = (
|
|||
// Only default index pattern type is supported here.
|
||||
// See data_enhanced for other type support.
|
||||
if (request.indexType) {
|
||||
throw new Error(`Unsupported index pattern type ${request.indexType}`);
|
||||
throw new KbnServerError(`Unsupported index pattern type ${request.indexType}`, 400);
|
||||
}
|
||||
|
||||
const search = async () => {
|
||||
|
|
|
@ -23,6 +23,7 @@ import { IRouter } from 'src/core/server';
|
|||
import { SearchRouteDependencies } from '../search_service';
|
||||
|
||||
import { getCallMsearch } from './call_msearch';
|
||||
import { reportServerError } from '../../../../kibana_utils/server';
|
||||
|
||||
/**
|
||||
* The msearch route takes in an array of searches, each consisting of header
|
||||
|
@ -69,15 +70,7 @@ export function registerMsearchRoute(router: IRouter, deps: SearchRouteDependenc
|
|||
const response = await callMsearch({ body: request.body });
|
||||
return res.ok(response);
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -22,6 +22,7 @@ import { schema } from '@kbn/config-schema';
|
|||
import type { IRouter } from 'src/core/server';
|
||||
import { getRequestAbortedSignal } from '../../lib';
|
||||
import { shimHitsTotal } from './shim_hits_total';
|
||||
import { reportServerError } from '../../../../kibana_utils/server';
|
||||
|
||||
export function registerSearchRoute(router: IRouter): void {
|
||||
router.post(
|
||||
|
@ -74,15 +75,7 @@ export function registerSearchRoute(router: IRouter): void {
|
|||
},
|
||||
});
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -106,15 +99,7 @@ export function registerSearchRoute(router: IRouter): void {
|
|||
await context.search!.cancel(id, { strategy });
|
||||
return res.ok();
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body.error,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -73,6 +73,7 @@ import {
|
|||
import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
|
||||
import { ConfigSchema } from '../../config';
|
||||
import { SessionService, IScopedSessionService, ISessionService } from './session';
|
||||
import { KbnServerError } from '../../../kibana_utils/server';
|
||||
|
||||
declare module 'src/core/server' {
|
||||
interface RequestHandlerContext {
|
||||
|
@ -305,7 +306,13 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
|
||||
private cancel = (id: string, options: ISearchOptions, deps: SearchStrategyDependencies) => {
|
||||
const strategy = this.getSearchStrategy(options.strategy);
|
||||
return strategy.cancel ? strategy.cancel(id, options, deps) : Promise.resolve();
|
||||
if (!strategy.cancel) {
|
||||
throw new KbnServerError(
|
||||
`Search strategy ${options.strategy} doesn't support cancellations`,
|
||||
400
|
||||
);
|
||||
}
|
||||
return strategy.cancel(id, options, deps);
|
||||
};
|
||||
|
||||
private getSearchStrategy = <
|
||||
|
@ -317,7 +324,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
this.logger.debug(`Get strategy ${name}`);
|
||||
const strategy = this.searchStrategies[name];
|
||||
if (!strategy) {
|
||||
throw new Error(`Search strategy ${name} not found`);
|
||||
throw new KbnServerError(`Search strategy ${name} not found`, 404);
|
||||
}
|
||||
return strategy;
|
||||
};
|
||||
|
|
|
@ -28,3 +28,5 @@ export {
|
|||
Set,
|
||||
url,
|
||||
} from '../common';
|
||||
|
||||
export { KbnServerError, reportServerError } from './report_server_error';
|
||||
|
|
39
src/plugins/kibana_utils/server/report_server_error.ts
Normal file
39
src/plugins/kibana_utils/server/report_server_error.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { KibanaResponseFactory } from 'kibana/server';
|
||||
import { KbnError } from '../common';
|
||||
|
||||
export class KbnServerError extends KbnError {
|
||||
constructor(message: string, public readonly statusCode: number) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function reportServerError(res: KibanaResponseFactory, err: any) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode ?? 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
|
@ -22,5 +22,6 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('search', () => {
|
||||
loadTestFile(require.resolve('./search'));
|
||||
loadTestFile(require.resolve('./msearch'));
|
||||
});
|
||||
}
|
||||
|
|
88
test/api_integration/apis/search/msearch.ts
Normal file
88
test/api_integration/apis/search/msearch.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('msearch', () => {
|
||||
describe('post', () => {
|
||||
it('should return 200 when correctly formatted searches are provided', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
searches: [
|
||||
{
|
||||
header: { index: 'foo' },
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200));
|
||||
|
||||
it('should return 400 if you provide malformed content', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
foo: false,
|
||||
})
|
||||
.expect(400));
|
||||
|
||||
it('should require you to provide an index for each request', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
searches: [
|
||||
{ header: { index: 'foo' }, body: {} },
|
||||
{ header: {}, body: {} },
|
||||
],
|
||||
})
|
||||
.expect(400));
|
||||
|
||||
it('should not require optional params', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo' }, body: {} }],
|
||||
})
|
||||
.expect(200));
|
||||
|
||||
it('should allow passing preference as a string', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo', preference: '_custom' }, body: {} }],
|
||||
})
|
||||
.expect(200));
|
||||
|
||||
it('should allow passing preference as a number', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo', preference: 123 }, body: {} }],
|
||||
})
|
||||
.expect(200));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -17,72 +17,101 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('msearch', () => {
|
||||
describe('search', () => {
|
||||
describe('post', () => {
|
||||
it('should return 200 when correctly formatted searches are provided', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
it('should return 200 when correctly formatted searches are provided', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/es`)
|
||||
.send({
|
||||
searches: [
|
||||
{
|
||||
header: { index: 'foo' },
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.expect(200));
|
||||
.expect(200);
|
||||
|
||||
it('should return 400 if you provide malformed content', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.send({
|
||||
foo: false,
|
||||
})
|
||||
.expect(400));
|
||||
expect(resp.body.isPartial).to.be(false);
|
||||
expect(resp.body.isRunning).to.be(false);
|
||||
expect(resp.body).to.have.property('rawResponse');
|
||||
});
|
||||
|
||||
it('should require you to provide an index for each request', async () =>
|
||||
it('should return 404 when if no strategy is provided', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.post(`/internal/search`)
|
||||
.send({
|
||||
searches: [
|
||||
{ header: { index: 'foo' }, body: {} },
|
||||
{ header: {}, body: {} },
|
||||
],
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400));
|
||||
.expect(404));
|
||||
|
||||
it('should not require optional params', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
it('should return 404 when if unknown strategy is provided', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/banana`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo' }, body: {} }],
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(200));
|
||||
.expect(404);
|
||||
expect(resp.body.message).to.contain('banana not found');
|
||||
});
|
||||
|
||||
it('should allow passing preference as a string', async () =>
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
it('should return 400 when index type is provided in OSS', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/es`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo', preference: '_custom' }, body: {} }],
|
||||
indexType: 'baad',
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(200));
|
||||
.expect(400);
|
||||
|
||||
it('should allow passing preference as a number', async () =>
|
||||
expect(resp.body.message).to.contain('Unsupported index pattern');
|
||||
});
|
||||
|
||||
it('should return 400 with a bad body', async () => {
|
||||
await supertest
|
||||
.post(`/internal/_msearch`)
|
||||
.post(`/internal/search/es`)
|
||||
.send({
|
||||
searches: [{ header: { index: 'foo', preference: 123 }, body: {} }],
|
||||
params: {
|
||||
body: {
|
||||
index: 'nope nope',
|
||||
bad_query: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(200));
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should return 404 when no search id provided', async () => {
|
||||
await supertest.delete(`/internal/search/es`).send().expect(404);
|
||||
});
|
||||
|
||||
it('should return 400 when trying a delete on a non supporting strategy', async () => {
|
||||
const resp = await supertest.delete(`/internal/search/es/123`).send().expect(400);
|
||||
expect(resp.body.message).to.contain("Search strategy es doesn't support cancellations");
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { reportServerError } from '../../../../../src/plugins/kibana_utils/server';
|
||||
|
||||
export function registerSessionRoutes(router: IRouter): void {
|
||||
router.post(
|
||||
|
@ -48,15 +49,7 @@ export function registerSessionRoutes(router: IRouter): void {
|
|||
body: response,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -78,16 +71,9 @@ export function registerSessionRoutes(router: IRouter): void {
|
|||
return res.ok({
|
||||
body: response,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
const err = e.output?.payload || e;
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -120,15 +106,7 @@ export function registerSessionRoutes(router: IRouter): void {
|
|||
body: response,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -148,16 +126,9 @@ export function registerSessionRoutes(router: IRouter): void {
|
|||
await context.search!.session.delete(id);
|
||||
|
||||
return res.ok();
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
const err = e.output?.payload || e;
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -185,15 +156,7 @@ export function registerSessionRoutes(router: IRouter): void {
|
|||
body: response,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.customError({
|
||||
statusCode: err.statusCode || 500,
|
||||
body: {
|
||||
message: err.message,
|
||||
attributes: {
|
||||
error: err.body?.error || err.message,
|
||||
},
|
||||
},
|
||||
});
|
||||
return reportServerError(res, err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
} from './request_utils';
|
||||
import { toAsyncKibanaSearchResponse } from './response_utils';
|
||||
import { AsyncSearchResponse } from './types';
|
||||
import { KbnServerError } from '../../../../../src/plugins/kibana_utils/server';
|
||||
|
||||
export const enhancedEsSearchStrategyProvider = (
|
||||
config$: Observable<SharedGlobalConfig>,
|
||||
|
@ -98,9 +99,13 @@ export const enhancedEsSearchStrategyProvider = (
|
|||
search: (request, options: IAsyncSearchOptions, deps) => {
|
||||
logger.debug(`search ${JSON.stringify(request.params) || request.id}`);
|
||||
|
||||
return request.indexType !== 'rollup'
|
||||
? asyncSearch(request, options, deps)
|
||||
: from(rollupSearch(request, options, deps));
|
||||
if (request.indexType === undefined) {
|
||||
return asyncSearch(request, options, deps);
|
||||
} else if (request.indexType === 'rollup') {
|
||||
return from(rollupSearch(request, options, deps));
|
||||
} else {
|
||||
throw new KbnServerError('Unknown indexType', 400);
|
||||
}
|
||||
},
|
||||
cancel: async (id, options, { esClient }) => {
|
||||
logger.debug(`cancel ${id}`);
|
||||
|
|
|
@ -10,6 +10,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
describe('apis', function () {
|
||||
this.tags('ciGroup6');
|
||||
|
||||
loadTestFile(require.resolve('./search'));
|
||||
loadTestFile(require.resolve('./es'));
|
||||
loadTestFile(require.resolve('./security'));
|
||||
loadTestFile(require.resolve('./spaces'));
|
||||
|
|
14
x-pack/test/api_integration/apis/search/index.ts
Normal file
14
x-pack/test/api_integration/apis/search/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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 }: FtrProviderContext) {
|
||||
describe('search', () => {
|
||||
loadTestFile(require.resolve('./search'));
|
||||
loadTestFile(require.resolve('./session'));
|
||||
});
|
||||
}
|
279
x-pack/test/api_integration/apis/search/search.ts
Normal file
279
x-pack/test/api_integration/apis/search/search.ts
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('search', () => {
|
||||
describe('post', () => {
|
||||
it('should return 200 with final response if wait_for_completion_timeout is long enough', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1000s',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id } = resp.body;
|
||||
expect(id).to.be(undefined);
|
||||
expect(resp.body.isPartial).to.be(false);
|
||||
expect(resp.body.isRunning).to.be(false);
|
||||
expect(resp.body).to.have.property('rawResponse');
|
||||
});
|
||||
|
||||
it('should return 200 with partial response if wait_for_completion_timeout is not long enough', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1ms',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id } = resp.body;
|
||||
expect(id).not.to.be(undefined);
|
||||
expect(resp.body.isPartial).to.be(true);
|
||||
expect(resp.body.isRunning).to.be(true);
|
||||
expect(resp.body).to.have.property('rawResponse');
|
||||
});
|
||||
|
||||
it('should retrieve results with id', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1ms',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id } = resp.body;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
const resp2 = await supertest
|
||||
.post(`/internal/search/ese/${id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({})
|
||||
.expect(200);
|
||||
|
||||
expect(resp2.body.id).not.to.be(undefined);
|
||||
expect(resp2.body.isPartial).to.be(false);
|
||||
expect(resp2.body.isRunning).to.be(false);
|
||||
});
|
||||
|
||||
it('should return 400 when unknown index type is provided', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
indexType: 'baad',
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(resp.body.message).to.contain('Unknown indexType');
|
||||
});
|
||||
|
||||
it('should return 400 if invalid id is provided', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese/123`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(resp.body.message).to.contain('illegal_argument_exception');
|
||||
});
|
||||
|
||||
it('should return 404 if unkown id is provided', async () => {
|
||||
const resp = await supertest
|
||||
.post(
|
||||
`/internal/search/ese/FkxOb21iV1g2VGR1S2QzaWVtRU9fMVEbc3JWeWc1VHlUdDZ6MENxcXlYVG1Fdzo2NDg4`
|
||||
)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(404);
|
||||
|
||||
expect(resp.body.message).to.contain('resource_not_found_exception');
|
||||
});
|
||||
|
||||
it('should return 400 with a bad body', async () => {
|
||||
await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
index: 'nope nope',
|
||||
bad_query: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rollup', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('hybrid/rollup');
|
||||
});
|
||||
after(async () => {
|
||||
await esArchiver.unload('hybrid/rollup');
|
||||
});
|
||||
|
||||
it('should return 400 if rollup search is called without index', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
indexType: 'rollup',
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(resp.body.message).to.contain('illegal_argument_exception');
|
||||
});
|
||||
|
||||
it('should return 400 if rollup search is without non-existent index', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
indexType: 'rollup',
|
||||
params: {
|
||||
index: 'banana',
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(resp.body.message).to.contain('illegal_argument_exception');
|
||||
});
|
||||
|
||||
it('should rollup search', async () => {
|
||||
await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
indexType: 'rollup',
|
||||
params: {
|
||||
index: 'rollup_logstash',
|
||||
size: 0,
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete', () => {
|
||||
it('should return 404 when no search id provided', async () => {
|
||||
await supertest.delete(`/internal/search/ese`).set('kbn-xsrf', 'foo').send().expect(404);
|
||||
});
|
||||
|
||||
it('should return 400 when trying a delete a bad id', async () => {
|
||||
const resp = await supertest
|
||||
.delete(`/internal/search/ese/123`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send()
|
||||
.expect(400);
|
||||
expect(resp.body.message).to.contain('illegal_argument_exception');
|
||||
});
|
||||
|
||||
it('should delete a search', async () => {
|
||||
const resp = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1ms',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id } = resp.body;
|
||||
await supertest
|
||||
.delete(`/internal/search/ese/${id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send()
|
||||
.expect(200);
|
||||
|
||||
// try to re-fetch
|
||||
await supertest
|
||||
.post(`/internal/search/ese/${id}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({})
|
||||
.expect(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
127
x-pack/test/api_integration/apis/search/session.ts
Normal file
127
x-pack/test/api_integration/apis/search/session.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
describe('search session', () => {
|
||||
describe('session management', () => {
|
||||
it('should create and get a session', async () => {
|
||||
const sessionId = `my-session-${Math.random()}`;
|
||||
await supertest
|
||||
.post(`/internal/session`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
name: 'My Session',
|
||||
appId: 'discover',
|
||||
expires: '123',
|
||||
urlGeneratorId: 'discover',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await supertest.get(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(200);
|
||||
});
|
||||
|
||||
it('should fail to delete an unknown session', async () => {
|
||||
await supertest.delete(`/internal/session/123`).set('kbn-xsrf', 'foo').expect(404);
|
||||
});
|
||||
|
||||
it('should create and delete a session', async () => {
|
||||
const sessionId = `my-session-${Math.random()}`;
|
||||
await supertest
|
||||
.post(`/internal/session`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
name: 'My Session',
|
||||
appId: 'discover',
|
||||
expires: '123',
|
||||
urlGeneratorId: 'discover',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await supertest.delete(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(200);
|
||||
|
||||
await supertest.get(`/internal/session/${sessionId}`).set('kbn-xsrf', 'foo').expect(404);
|
||||
});
|
||||
|
||||
it('should sync search ids into session', async () => {
|
||||
const sessionId = `my-session-${Math.random()}`;
|
||||
|
||||
// run search
|
||||
const searchRes1 = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
term: {
|
||||
agent: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1ms',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id: id1 } = searchRes1.body;
|
||||
|
||||
// create session
|
||||
await supertest
|
||||
.post(`/internal/session`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
name: 'My Session',
|
||||
appId: 'discover',
|
||||
expires: '123',
|
||||
urlGeneratorId: 'discover',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
// run search
|
||||
const searchRes2 = await supertest
|
||||
.post(`/internal/search/ese`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
sessionId,
|
||||
params: {
|
||||
body: {
|
||||
query: {
|
||||
match_all: {},
|
||||
},
|
||||
},
|
||||
wait_for_completion_timeout: '1ms',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const { id: id2 } = searchRes2.body;
|
||||
|
||||
// wait 10 seconds for ids to be synced
|
||||
// TODO: make the refresh interval dynamic, so we can speed it up!
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
|
||||
const resp = await supertest
|
||||
.get(`/internal/session/${sessionId}`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.expect(200);
|
||||
|
||||
const { idMapping } = resp.body.attributes;
|
||||
|
||||
expect(Object.values(idMapping)).to.contain(id1);
|
||||
expect(Object.values(idMapping)).to.contain(id2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -30,6 +30,7 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi
|
|||
'--telemetry.optIn=true',
|
||||
'--xpack.fleet.enabled=true',
|
||||
'--xpack.fleet.agents.pollingRequestTimeout=5000', // 5 seconds
|
||||
'--xpack.data_enhanced.search.sendToBackground.enabled=true', // enable WIP send to background UI
|
||||
],
|
||||
},
|
||||
esTestCluster: {
|
||||
|
|
Loading…
Reference in a new issue