[Data Discovery] Run API integration tests in Serverless (#163550)

## Summary

This PR copies the Data Discovery API integration tests to
`test_serverless`. They are currently in the common folder and will run
once for each project type in CI, but we could instead move them to a
specific project if we don't want to run them three times each.

In the future these should run as deployment-agnostic tests, but support
does not yet exist (see #161574), so in the meantime they've been
duplicated and modified in place. I've left `TODO` comments where test
files have been modified so we know what needs to be addressed once they
are converted to deployment-agnostic tests.

Part of #162347.

### Checklist

- [ ] ~Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~
- [ ]
~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials~
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] ~Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard
accessibility](https://webaim.org/techniques/keyboard/))~
- [ ] ~Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))~
- [ ] ~If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~
- [ ] ~This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))~
- [ ] ~This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)~

### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Davis McPhee 2023-08-29 10:22:48 -03:00 committed by GitHub
parent 4f87b43940
commit c288c8b9bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 6028 additions and 0 deletions

6
.github/CODEOWNERS vendored
View file

@ -826,6 +826,12 @@ packages/kbn-yarn-lock-validator @elastic/kibana-operations
/x-pack/test/stack_functional_integration/apps/ccs/ccs_discover.js @elastic/kibana-data-discovery
/x-pack/test/stack_functional_integration/apps/management/_index_pattern_create.js @elastic/kibana-data-discovery
/x-pack/test/upgrade/apps/discover @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/data_views @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/data_view_field_editor @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/kql_telemetry @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/scripts_tests @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/search_oss @elastic/kibana-data-discovery
/x-pack/test_serverless/api_integration/test_suites/common/search_xpack @elastic/kibana-data-discovery
# Visualizations
/src/plugins/visualize/ @elastic/kibana-visualizations

View file

@ -0,0 +1,173 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { getErrorCodeFromErrorReason } from '@kbn/data-view-field-editor-plugin/public/lib/runtime_field_validation';
import {
FIELD_PREVIEW_PATH,
INITIAL_REST_VERSION,
} from '@kbn/data-view-field-editor-plugin/common/constants';
import type { FtrProviderContext } from '../../../ftr_provider_context';
const INDEX_NAME = 'api-integration-test-field-preview';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const es = getService('es');
const svlCommonApi = getService('svlCommonApi');
const document = { foo: 1, bar: 'hello' };
const createIndex = async () => {
await es.indices.create({
index: INDEX_NAME,
body: {
mappings: {
properties: {
foo: {
type: 'integer',
},
bar: {
type: 'keyword',
},
},
},
},
});
};
const deleteIndex = async () => {
await es.indices.delete({
index: INDEX_NAME,
});
};
describe('Field preview', function () {
before(async () => await createIndex());
after(async () => await deleteIndex());
describe('should return the script value', () => {
const tests = [
{
context: 'keyword_field',
script: {
source: 'emit("test")',
},
expected: 'test',
},
{
context: 'long_field',
script: {
source: 'emit(doc["foo"].value + 1)',
},
expected: 2,
},
{
context: 'keyword_field',
script: {
source: 'emit(doc["bar"].value + " world")',
},
expected: 'hello world',
},
];
tests.forEach((test) => {
it(`> ${test.context}`, async () => {
const payload = {
script: test.script,
document,
context: test.context,
index: INDEX_NAME,
};
const { body: response } = await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send(payload)
.set('kbn-xsrf', 'xxx')
.expect(200);
expect(response.values).eql([test.expected]);
});
});
});
describe('payload validation', () => {
it('should require a script', async () => {
await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
context: 'keyword_field',
index: INDEX_NAME,
})
.set('kbn-xsrf', 'xxx')
.expect(400);
});
it('should require a context', async () => {
await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
script: { source: 'emit("hello")' },
index: INDEX_NAME,
})
.set('kbn-xsrf', 'xxx')
.expect(400);
});
it('should require an index', async () => {
await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
script: { source: 'emit("hello")' },
context: 'keyword_field',
})
.set('kbn-xsrf', 'xxx')
.expect(400);
});
});
describe('Error messages', () => {
// As ES does not return error codes we will add a test to make sure its error message string
// does not change overtime as we rely on it to extract our own error code.
// If this test fail we'll need to update the "getErrorCodeFromErrorReason()" handler
// TODO: `response.error?.caused_by?.reason` returns
// `class_cast_exception: Cannot cast from [int] to [java.lang.String].`
// in Serverless, which causes `getErrorCodeFromErrorReason` to fail
it.skip('should detect a script casting error', async () => {
const { body: response } = await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
script: { source: 'emit(123)' }, // We send a long but the type is "keyword"
context: 'keyword_field',
index: INDEX_NAME,
})
.set('kbn-xsrf', 'xxx');
const errorCode = getErrorCodeFromErrorReason(response.error?.caused_by?.reason);
expect(errorCode).be('CAST_ERROR');
});
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('index pattern field editor', () => {
loadTestFile(require.resolve('./field_preview'));
});
}

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
DATA_VIEW_PATH_LEGACY,
SERVICE_KEY_LEGACY,
DATA_VIEW_PATH,
SERVICE_KEY,
SERVICE_PATH,
SERVICE_PATH_LEGACY,
} from '@kbn/data-views-plugin/server';
const legacyConfig = {
name: 'legacy index pattern api',
path: DATA_VIEW_PATH_LEGACY,
basePath: SERVICE_PATH_LEGACY,
serviceKey: SERVICE_KEY_LEGACY,
};
export const dataViewConfig = {
name: 'data view api',
path: DATA_VIEW_PATH,
basePath: SERVICE_PATH,
serviceKey: SERVICE_KEY,
};
export const configArray = [legacyConfig, dataViewConfig];

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('create_index_pattern', () => {
loadTestFile(require.resolve('./validation'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,334 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('can create an index_pattern with just a title', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response.status).to.be(200);
});
it('returns back the created index_pattern object', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(typeof response.body[config.serviceKey]).to.be('object');
expect(response.body[config.serviceKey].title).to.be(title);
expect(typeof response.body[config.serviceKey].id).to.be('string');
expect(response.body[config.serviceKey].id.length > 0).to.be(true);
});
it('can specify primitive optional attributes when creating an index pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const id = `test-id-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
id,
type: 'test-type',
timeFieldName: 'test-timeFieldName',
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].title).to.be(title);
expect(response.body[config.serviceKey].id).to.be(id);
expect(response.body[config.serviceKey].type).to.be('test-type');
expect(response.body[config.serviceKey].timeFieldName).to.be('test-timeFieldName');
});
it('can specify optional sourceFilters attribute when creating an index pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
sourceFilters: [
{
value: 'foo',
},
],
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].title).to.be(title);
expect(response.body[config.serviceKey].sourceFilters[0].value).to.be('foo');
});
describe('creating fields', () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('can specify optional fields attribute when creating an index pattern', async () => {
const title = `basic_index*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
fields: {
foo: {
name: 'foo',
// TODO: Scripted fields code dropped since they are not supported in Serverless
customLabel: 'Custom Label',
},
},
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].title).to.be(title);
expect(response.body[config.serviceKey].fields.foo.name).to.be('foo');
// TODO: Scripted fields code dropped since they are not supported in Serverless
expect(response.body[config.serviceKey].fields.foo.customLabel).to.be('Custom Label');
expect(response.body[config.serviceKey].fields.bar.name).to.be('bar'); // created from es index
expect(response.body[config.serviceKey].fields.bar.type).to.be('boolean');
});
// TODO: Scripted fields code dropped since they are not supported in Serverless
it('can add fields created from es index', async () => {
const title = `basic_index*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
fields: {
foo: {
name: 'foo',
type: 'string',
},
fake: {
name: 'fake',
type: 'string',
},
},
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].title).to.be(title);
expect(response.body[config.serviceKey].fields.foo.name).to.be('foo');
expect(response.body[config.serviceKey].fields.foo.type).to.be('number'); // picked up from index
expect(response.body[config.serviceKey].fields.fake).to.be(undefined); // not in index, so not created
});
it('can add runtime fields', async () => {
const title = `basic_index*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].title).to.be(title);
expect(response.body[config.serviceKey].runtimeFieldMap.runtimeFoo.type).to.be(
'keyword'
);
expect(response.body[config.serviceKey].runtimeFieldMap.runtimeFoo.script.source).to.be(
'emit(doc["foo"].value)'
);
});
});
it('can specify optional typeMeta attribute when creating an index pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
typeMeta: {},
},
});
expect(response.status).to.be(200);
});
it('can specify optional fieldFormats attribute when creating an index pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldFormats: {
foo: {
id: 'test-id',
params: {},
},
},
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].fieldFormats.foo.id).to.be('test-id');
expect(response.body[config.serviceKey].fieldFormats.foo.params).to.eql({});
});
it('can specify optional fieldFormats attribute when creating an index pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldAttrs: {
foo: {
count: 123,
customLabel: 'test',
},
},
},
});
expect(response.status).to.be(200);
expect(response.body[config.serviceKey].fieldAttrs.foo.count).to.be(123);
expect(response.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test');
});
describe('when creating index pattern with existing name', () => {
it('returns error, by default', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response1.status).to.be(200);
expect(response2.status).to.be(400);
});
it('succeeds, override flag is set', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
timeFieldName: 'foo',
},
});
const response2 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
timeFieldName: 'bar',
},
});
expect(response1.status).to.be(200);
expect(response2.status).to.be(200);
expect(response1.body[config.serviceKey].timeFieldName).to.be('foo');
expect(response2.body[config.serviceKey].timeFieldName).to.be('bar');
expect(response1.body[config.serviceKey].id).to.be(
response1.body[config.serviceKey].id
);
});
});
});
});
// TODO: Removed spaces tests since non-default spaces aren't supported in Serverless
});
}

View file

@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('validation', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns error when index_pattern object is not provided', async () => {
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
'[request body]: expected a plain object value, but found [null] instead.'
);
});
it('returns error on empty index_pattern object', async () => {
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
`[request body.${config.serviceKey}.title]: expected value of type [string] but got [undefined]`
);
});
it('returns error when "override" parameter is not a boolean', async () => {
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: 123,
[config.serviceKey]: {
title: 'foo',
},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
'[request body.override]: expected value of type [boolean] but got [number]'
);
});
it('returns error when "refresh_fields" parameter is not a boolean', async () => {
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
refresh_fields: 123,
[config.serviceKey]: {
title: 'foo',
},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
'[request body.refresh_fields]: expected value of type [boolean] but got [number]'
);
});
it('returns an error when unknown runtime field type', async () => {
const title = `basic_index*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'wrong-type',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response.status).to.be(400);
});
});
});
});
}

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.delete(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest
.delete(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('delete_index_pattern', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,89 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('deletes an index_pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response2.status).to.be(200);
const response3 = await supertest
.delete(`${config.path}/${response1.body[config.serviceKey].id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
const response4 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response4.status).to.be(404);
});
});
it('returns nothing', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const response2 = await supertest
.delete(`${config.path}/${response1.body[config.serviceKey].id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
// verify empty response
expect(Object.keys(response2.body).length).to.be(0);
});
});
});
}

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('get_index_pattern', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('can retrieve an index_pattern', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response2.body[config.serviceKey].title).to.be(title);
});
});
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('get_data_views', () => {
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,29 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { dataViewConfig } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
describe('get data views api', () => {
it('returns list of data views', async () => {
const response = await supertest
.get(dataViewConfig.basePath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(200);
expect(response.body).to.have.property(dataViewConfig.serviceKey);
expect(response.body[dataViewConfig.serviceKey]).to.be.an('array');
});
});
});
}

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('index_pattern_crud', () => {
loadTestFile(require.resolve('./create_data_view'));
loadTestFile(require.resolve('./get_data_view'));
loadTestFile(require.resolve('./delete_data_view'));
loadTestFile(require.resolve('./update_data_view'));
loadTestFile(require.resolve('./get_data_views'));
});
}

View file

@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns error when index_pattern object is not provided', async () => {
const response = await supertest
.post(`${config.path}/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
'[request body]: expected a plain object value, but found [null] instead.'
);
});
it('returns error on non-existing index_pattern', async () => {
const response = await supertest
.post(`${config.path}/non-existing-index-pattern`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {},
});
expect(response.status).to.be(404);
expect(response.body.statusCode).to.be(404);
expect(response.body.message).to.be(
'Saved object [index-pattern/non-existing-index-pattern] not found'
);
});
it('returns error when "refresh_fields" parameter is not a boolean', async () => {
const response = await supertest
.post(`${config.path}/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
refresh_fields: 123,
[config.serviceKey]: {
title: 'foo',
},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
'[request body.refresh_fields]: expected value of type [boolean] but got [number]'
);
});
it('returns success when update patch is empty', async () => {
const title1 = `foo-${Date.now()}-${Math.random()}*`;
const response = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title: title1,
},
});
const id = response.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {},
});
expect(response2.status).to.be(200);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('update_index_pattern', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,445 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('can update data view title', async () => {
const title1 = `foo-${Date.now()}-${Math.random()}*`;
const title2 = `bar-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title: title1,
},
});
expect(response1.body[config.serviceKey].title).to.be(title1);
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title: title2,
},
});
expect(response2.body[config.serviceKey].title).to.be(title2);
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].title).to.be(title2);
});
it('can update data view name', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const name1 = 'good data view name';
const name2 = 'better data view name';
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
name: name1,
},
});
expect(response1.body[config.serviceKey].name).to.be(name1);
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
name: name2,
},
});
expect(response2.body[config.serviceKey].name).to.be(name2);
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].name).to.be(name2);
});
it('can update data view timeFieldName', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
timeFieldName: 'timeFieldName1',
},
});
expect(response1.body[config.serviceKey].timeFieldName).to.be('timeFieldName1');
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
timeFieldName: 'timeFieldName2',
},
});
expect(response2.body[config.serviceKey].timeFieldName).to.be('timeFieldName2');
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].timeFieldName).to.be('timeFieldName2');
});
it('can update data view sourceFilters', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
sourceFilters: [
{
value: 'foo',
},
],
},
});
expect(response1.body[config.serviceKey].sourceFilters).to.eql([
{
value: 'foo',
},
]);
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
sourceFilters: [
{
value: 'bar',
},
{
value: 'baz',
},
],
},
});
expect(response2.body[config.serviceKey].sourceFilters).to.eql([
{
value: 'bar',
},
{
value: 'baz',
},
]);
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].sourceFilters).to.eql([
{
value: 'bar',
},
{
value: 'baz',
},
]);
});
it('can update data view fieldFormats', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldFormats: {
foo: {
id: 'foo',
params: {
bar: 'baz',
},
},
},
},
});
expect(response1.body[config.serviceKey].fieldFormats).to.eql({
foo: {
id: 'foo',
params: {
bar: 'baz',
},
},
});
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
fieldFormats: {
a: {
id: 'a',
params: {
b: 'v',
},
},
},
},
});
expect(response2.body[config.serviceKey].fieldFormats).to.eql({
a: {
id: 'a',
params: {
b: 'v',
},
},
});
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].fieldFormats).to.eql({
a: {
id: 'a',
params: {
b: 'v',
},
},
});
});
it('can update data view type', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
type: 'foo',
},
});
expect(response1.body[config.serviceKey].type).to.be('foo');
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
type: 'bar',
},
});
expect(response2.body[config.serviceKey].type).to.be('bar');
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].type).to.be('bar');
});
it('can update data view typeMeta', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
typeMeta: { foo: 'bar' },
},
});
expect(response1.body[config.serviceKey].typeMeta).to.eql({ foo: 'bar' });
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
typeMeta: { foo: 'baz' },
},
});
expect(response2.body[config.serviceKey].typeMeta).to.eql({ foo: 'baz' });
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].typeMeta).to.eql({ foo: 'baz' });
});
it('can update multiple data view fields at once', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
timeFieldName: 'timeFieldName1',
typeMeta: { foo: 'bar' },
},
});
expect(response1.body[config.serviceKey].timeFieldName).to.be('timeFieldName1');
expect(response1.body[config.serviceKey].typeMeta.foo).to.be('bar');
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
timeFieldName: 'timeFieldName2',
typeMeta: { baz: 'qux' },
},
});
expect(response2.body[config.serviceKey].timeFieldName).to.be('timeFieldName2');
expect(response2.body[config.serviceKey].typeMeta.baz).to.be('qux');
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].timeFieldName).to.be('timeFieldName2');
expect(response3.body[config.serviceKey].typeMeta.baz).to.be('qux');
});
it('can update runtime fields', async () => {
const title = `basic_index*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].title).to.be(title);
expect(response1.body[config.serviceKey].runtimeFieldMap.runtimeFoo.type).to.be(
'keyword'
);
expect(response1.body[config.serviceKey].runtimeFieldMap.runtimeFoo.script.source).to.be(
'emit(doc["foo"].value)'
);
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
runtimeFieldMap: {
runtimeBar: {
type: 'keyword',
script: {
source: 'emit(doc["foo"].value)',
},
},
},
},
});
expect(response2.body[config.serviceKey].runtimeFieldMap.runtimeBar.type).to.be(
'keyword'
);
expect(response2.body[config.serviceKey].runtimeFieldMap.runtimeFoo).to.be(undefined);
const response3 = await supertest
.get(`${config.path}/${id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.body[config.serviceKey].runtimeFieldMap.runtimeBar.type).to.be(
'keyword'
);
expect(response3.body[config.serviceKey].runtimeFieldMap.runtimeFoo).to.be(undefined);
});
});
});
});
}

View file

@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
import { configArray } from '../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('default index pattern api', () => {
configArray.forEach((config) => {
describe(config.name, () => {
const newId = () => `default-id-${Date.now()}-${Math.random()}`;
it('can set default index pattern', async () => {
const defaultId = newId();
const defaultPath = `${config.basePath}/default`;
const serviceKeyId = `${config.serviceKey}_id`;
const response1 = await supertest
.post(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[serviceKeyId]: defaultId,
force: true,
});
expect(response1.status).to.be(200);
expect(response1.body.acknowledged).to.be(true);
const response2 = await supertest
.get(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response2.status).to.be(200);
expect(response2.body[serviceKeyId]).to.be(defaultId);
const response3 = await supertest
.post(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[serviceKeyId]: newId(),
// no force this time, so this new default shouldn't be set
});
expect(response3.status).to.be(200);
const response4 = await supertest
.get(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response4.body[serviceKeyId]).to.be(defaultId); // original default id is used
const response5 = await supertest
.post(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[serviceKeyId]: null,
force: true,
});
expect(response5.status).to.be(200);
const response6 = await supertest
.get(defaultPath)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response6.body[serviceKeyId]).to.be(null);
});
});
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('default index pattern', () => {
loadTestFile(require.resolve('./default_index_pattern'));
});
}

View file

@ -0,0 +1,144 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { errors as esErrors } from '@elastic/elasticsearch';
import Boom from '@hapi/boom';
import {
isEsIndexNotFoundError,
createNoMatchingIndicesError,
isNoMatchingIndicesError,
convertEsError,
} from '@kbn/data-views-plugin/server/fetcher/lib/errors';
import { getIndexNotFoundError, getDocNotFoundError } from './lib';
export default function ({ getService }) {
const es = getService('es');
const esArchiver = getService('esArchiver');
describe('index_patterns/* error handler', () => {
let indexNotFoundError;
let docNotFoundError;
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
indexNotFoundError = await getIndexNotFoundError(es);
docNotFoundError = await getDocNotFoundError(es);
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
describe('isEsIndexNotFoundError()', () => {
it('identifies index not found errors', () => {
if (!isEsIndexNotFoundError(indexNotFoundError)) {
throw new Error(`Expected isEsIndexNotFoundError(indexNotFoundError) to be true`);
}
});
it('rejects doc not found errors', () => {
if (isEsIndexNotFoundError(docNotFoundError)) {
throw new Error(`Expected isEsIndexNotFoundError(docNotFoundError) to be true`);
}
});
});
describe('createNoMatchingIndicesError()', () => {
it('returns a boom error', () => {
const error = createNoMatchingIndicesError();
if (!error || !error.isBoom) {
throw new Error(`expected ${error} to be a Boom error`);
}
});
it('sets output code to "no_matching_indices"', () => {
const error = createNoMatchingIndicesError();
expect(error.output.payload).to.have.property('code', 'no_matching_indices');
});
});
describe('isNoMatchingIndicesError()', () => {
it('returns true for errors from createNoMatchingIndicesError()', () => {
if (!isNoMatchingIndicesError(createNoMatchingIndicesError())) {
throw new Error(
'Expected isNoMatchingIndicesError(createNoMatchingIndicesError()) to be true'
);
}
});
it('returns false for indexNotFoundError', () => {
if (isNoMatchingIndicesError(indexNotFoundError)) {
throw new Error('expected isNoMatchingIndicesError(indexNotFoundError) to be false');
}
});
it('returns false for docNotFoundError', async () => {
if (isNoMatchingIndicesError(docNotFoundError)) {
throw new Error('expected isNoMatchingIndicesError(docNotFoundError) to be false');
}
});
});
describe('convertEsError()', () => {
const indices = ['foo', 'bar'];
it('converts indexNotFoundErrors into NoMatchingIndices errors', async () => {
const converted = convertEsError(indices, indexNotFoundError);
if (!isNoMatchingIndicesError(converted)) {
throw new Error(
'expected convertEsError(indexNotFoundError) to return NoMatchingIndices error'
);
}
});
it('wraps other errors in Boom', async () => {
const error = new esErrors.ResponseError({
body: {
root_cause: [
{
type: 'security_exception',
reason: 'action [indices:data/read/field_caps] is unauthorized for user [standard]',
},
],
type: 'security_exception',
reason: 'action [indices:data/read/field_caps] is unauthorized for user [standard]',
},
statusCode: 403,
});
expect(error).to.not.have.property('isBoom');
const converted = convertEsError(indices, error);
expect(converted).to.have.property('isBoom');
expect(converted.output.statusCode).to.be(403);
});
it('handles errors that are already Boom errors', () => {
const error = new Error();
error.statusCode = 401;
const boomError = Boom.boomify(error, { statusCode: error.statusCode });
const converted = convertEsError(indices, boomError);
expect(converted.output.statusCode).to.be(401);
});
it('preserves headers from Boom errors', () => {
const error = new Error();
error.statusCode = 401;
const boomError = Boom.boomify(error, { statusCode: error.statusCode });
const wwwAuthenticate = 'Basic realm="Authorization Required"';
boomError.output.headers['WWW-Authenticate'] = wwwAuthenticate;
const converted = convertEsError(indices, boomError);
expect(converted.output.headers['WWW-Authenticate']).to.be(wwwAuthenticate);
});
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('index_patterns/service/lib', () => {
loadTestFile(require.resolve('./errors'));
});
}

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { Client } from '@elastic/elasticsearch';
export async function getIndexNotFoundError(es: Client) {
try {
await es.indices.get({
index: 'SHOULD NOT EXIST',
});
} catch (err) {
expect(err).to.have.property('statusCode', 404); // basic check
return err;
}
throw new Error('Expected es.indices.get() call to fail');
}
export async function getDocNotFoundError(es: Client) {
try {
await es.get({
index: 'basic_index',
id: '1234',
});
} catch (err) {
expect(err).to.have.property('statusCode', 404); // basic check
return err;
}
throw new Error('Expected es.get() call to fail');
}

View file

@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { getIndexNotFoundError, getDocNotFoundError } from './get_es_errors';

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('fields_api', () => {
loadTestFile(require.resolve('./update_fields'));
});
}

View file

@ -0,0 +1,91 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.post(`${config.path}/${id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {},
},
});
expect(response.status).to.be(404);
});
it('returns error when "fields" payload attribute is invalid', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: 123,
});
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be(
'[request body.fields]: expected value of type [object] but got [number]'
);
});
it('returns error if not changes are specified', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {},
bar: {},
baz: {},
},
});
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be('Change set is empty.');
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('update_fields', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,523 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
const basicIndex = 'ba*ic_index';
let indexPattern: any;
configArray.forEach((config) => {
describe(config.name, () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
indexPattern = (
await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title: basicIndex,
},
})
).body[config.serviceKey];
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
if (indexPattern) {
await supertest
.delete(`${config.path}/${indexPattern.id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
}
});
it('can update multiple fields', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined);
expect(response1.body[config.serviceKey].fieldAttrs.bar).to.be(undefined);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
count: 123,
customLabel: 'test',
},
bar: {
count: 456,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(123);
expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test');
expect(response2.body[config.serviceKey].fieldAttrs.bar.count).to.be(456);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(123);
expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('test');
expect(response3.body[config.serviceKey].fieldAttrs.bar.count).to.be(456);
});
describe('count', () => {
it('can set field "count" attribute on non-existing field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
count: 123,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(123);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(123);
});
it('can update "count" attribute in index_pattern attribute map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldAttrs: {
foo: {
count: 1,
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo.count).to.be(1);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
count: 2,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(2);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(2);
});
it('can delete "count" attribute from index_pattern attribute map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldAttrs: {
foo: {
count: 1,
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo.count).to.be(1);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
count: null,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.count).to.be(undefined);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.count).to.be(undefined);
});
});
describe('customLabel', () => {
it('can set field "customLabel" attribute on non-existing field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
customLabel: 'foo',
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo');
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo');
});
it('can update "customLabel" attribute in index_pattern attribute map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldAttrs: {
foo: {
customLabel: 'foo',
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo');
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
customLabel: 'bar',
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('bar');
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('bar');
});
it('can delete "customLabel" attribute from index_pattern attribute map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldAttrs: {
foo: {
customLabel: 'foo',
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be('foo');
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
customLabel: null,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be(undefined);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldAttrs.foo.customLabel).to.be(undefined);
});
it('can set field "customLabel" attribute on an existing field', async () => {
await supertest
.post(`${config.path}/${indexPattern.id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
customLabel: 'baz',
},
},
});
const response1 = await supertest
.get(`${config.path}/${indexPattern.id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fields.foo.customLabel).to.be('baz');
});
});
describe('format', () => {
it('can set field "format" attribute on non-existing field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldFormats.foo).to.be(undefined);
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
format: {
id: 'bar',
params: { baz: 'qux' },
},
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldFormats.foo).to.eql({
id: 'bar',
params: { baz: 'qux' },
});
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldFormats.foo).to.eql({
id: 'bar',
params: { baz: 'qux' },
});
});
it('can update "format" attribute in index_pattern format map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
fieldFormats: {
foo: {
id: 'bar',
params: {
baz: 'qux',
},
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body[config.serviceKey].fieldFormats.foo).to.eql({
id: 'bar',
params: {
baz: 'qux',
},
});
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
format: {
id: 'bar-2',
params: { baz: 'qux-2' },
},
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldFormats.foo).to.eql({
id: 'bar-2',
params: { baz: 'qux-2' },
});
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldFormats.foo).to.eql({
id: 'bar-2',
params: { baz: 'qux-2' },
});
});
it('can remove "format" attribute from index_pattern format map', async () => {
const response2 = await supertest
.post(`${config.path}/${indexPattern.id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
foo: {
format: null,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey].fieldFormats.foo).to.be(undefined);
const response3 = await supertest
.get(`${config.path}/${indexPattern.id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey].fieldFormats.foo).to.be(undefined);
});
// TODO: Scripted fields code dropped since they are not supported in Serverless
});
});
});
});
}

View file

@ -0,0 +1,83 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants';
import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('conflicts', () => {
before(() =>
esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/conflicts')
);
after(() =>
esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/conflicts')
);
it('flags fields with mismatched types as conflicting', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'logs-*' })
.expect(200)
.then((resp) => {
expect(resp.body).to.eql({
fields: [
{
name: '@timestamp',
type: 'date',
esTypes: ['date'],
aggregatable: true,
searchable: true,
readFromDocValues: true,
metadata_field: false,
},
{
name: 'number_conflict',
type: 'number',
esTypes: ['float', 'integer'],
aggregatable: true,
searchable: true,
readFromDocValues: true,
metadata_field: false,
},
{
name: 'string_conflict',
type: 'string',
esTypes: ['keyword', 'text'],
aggregatable: true,
searchable: true,
readFromDocValues: true,
metadata_field: false,
},
{
name: 'success',
type: 'conflict',
esTypes: ['keyword', 'boolean'],
aggregatable: true,
searchable: true,
readFromDocValues: false,
conflictDescriptions: {
boolean: ['logs-2017.01.02'],
keyword: ['logs-2017.01.01'],
},
metadata_field: false,
},
],
indices: ['logs-2017.01.01', 'logs-2017.01.02'],
});
}));
});
}

View file

@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants';
import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const es = getService('es');
const svlCommonApi = getService('svlCommonApi');
describe('filter fields', () => {
before(async () => {
await es.index({
index: 'helloworld1',
refresh: true,
id: 'helloworld',
body: { hello: 'world' },
});
await es.index({
index: 'helloworld2',
refresh: true,
id: 'helloworld2',
body: { bye: 'world' },
});
});
it('can filter', async () => {
const a = await supertest
.put(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'helloworld*' })
.send({ index_filter: { exists: { field: 'bye' } } });
const fieldNames = a.body.fields.map((fld: { name: string }) => fld.name);
expect(fieldNames.indexOf('bye') > -1).to.be(true);
expect(fieldNames.indexOf('hello') === -1).to.be(true);
});
});
}

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('index_patterns/_fields_for_wildcard route', () => {
loadTestFile(require.resolve('./params'));
loadTestFile(require.resolve('./conflicts'));
loadTestFile(require.resolve('./response'));
loadTestFile(require.resolve('./filter'));
});
}

View file

@ -0,0 +1,172 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants';
import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const randomness = getService('randomness');
const svlCommonApi = getService('svlCommonApi');
describe('params', () => {
before(() =>
esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index')
);
after(() =>
esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/basic_index')
);
it('requires a pattern query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({})
.expect(400));
it('accepts include_unmapped param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
include_unmapped: true,
})
.expect(200));
it('rejects unexpected query params', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: randomness.word(),
[randomness.word()]: randomness.word(),
})
.expect(400));
describe('fields', () => {
it('accepts a JSON formatted fields query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
fields: JSON.stringify(['baz']),
})
.expect(200));
it('accepts meta_fields query param in string array', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
fields: ['baz', 'foo'],
})
.expect(200));
it('accepts single array fields query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
fields: ['baz'],
})
.expect(200));
it('accepts single fields query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
fields: 'baz',
})
.expect(200));
it('rejects a comma-separated list of fields', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
fields: 'foo,bar',
})
.expect(400));
});
describe('meta_fields', () => {
it('accepts a JSON formatted meta_fields query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
meta_fields: JSON.stringify(['meta']),
})
.expect(200));
it('accepts meta_fields query param in string array', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
meta_fields: ['_id', 'meta'],
})
.expect(200));
it('accepts single meta_fields query param', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
meta_fields: ['_id'],
})
.expect(200));
it('rejects a comma-separated list of meta_fields', () =>
supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: '*',
meta_fields: 'foo,bar',
})
.expect(400));
});
});
}

View file

@ -0,0 +1,243 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants';
import { FIELDS_FOR_WILDCARD_PATH } from '@kbn/data-views-plugin/common/constants';
import expect from '@kbn/expect';
import { sortBy } from 'lodash';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
const ensureFieldsAreSorted = (resp: { body: { fields: { name: string } } }) => {
expect(resp.body.fields).to.eql(sortBy(resp.body.fields, 'name'));
};
const testFields = [
{
type: 'boolean',
esTypes: ['boolean'],
searchable: true,
aggregatable: true,
name: 'bar',
readFromDocValues: true,
metadata_field: false,
},
{
type: 'string',
esTypes: ['text'],
searchable: true,
aggregatable: false,
name: 'baz',
readFromDocValues: false,
metadata_field: false,
},
{
type: 'string',
esTypes: ['keyword'],
searchable: true,
aggregatable: true,
name: 'baz.keyword',
readFromDocValues: true,
subType: { multi: { parent: 'baz' } },
metadata_field: false,
},
{
type: 'number',
esTypes: ['long'],
searchable: true,
aggregatable: true,
name: 'foo',
readFromDocValues: true,
metadata_field: false,
},
{
aggregatable: true,
esTypes: ['keyword'],
name: 'nestedField.child',
readFromDocValues: true,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
metadata_field: false,
},
];
describe('fields_for_wildcard_route response', () => {
before(() =>
esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index')
);
after(() =>
esArchiver.unload('test/api_integration/fixtures/es_archiver/index_patterns/basic_index')
);
it('returns a flattened version of the fields in es', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'basic_index' })
.expect(200, {
fields: testFields,
indices: ['basic_index'],
})
.then(ensureFieldsAreSorted);
});
it('returns a single field as requested', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'basic_index', fields: JSON.stringify(['bar']) })
.expect(200, {
fields: [testFields[0]],
indices: ['basic_index'],
});
});
it('always returns a field for all passed meta fields', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: 'basic_index',
meta_fields: JSON.stringify(['_id', '_source', 'crazy_meta_field']),
})
.expect(200, {
fields: [
{
aggregatable: false,
name: '_id',
esTypes: ['_id'],
readFromDocValues: false,
searchable: true,
type: 'string',
metadata_field: true,
},
{
aggregatable: false,
name: '_source',
esTypes: ['_source'],
readFromDocValues: false,
searchable: false,
type: '_source',
metadata_field: true,
},
{
type: 'boolean',
esTypes: ['boolean'],
searchable: true,
aggregatable: true,
name: 'bar',
readFromDocValues: true,
metadata_field: false,
},
{
aggregatable: false,
name: 'baz',
esTypes: ['text'],
readFromDocValues: false,
searchable: true,
type: 'string',
metadata_field: false,
},
{
type: 'string',
esTypes: ['keyword'],
searchable: true,
aggregatable: true,
name: 'baz.keyword',
readFromDocValues: true,
subType: { multi: { parent: 'baz' } },
metadata_field: false,
},
{
aggregatable: false,
name: 'crazy_meta_field',
readFromDocValues: false,
searchable: false,
type: 'string',
metadata_field: true,
},
{
type: 'number',
esTypes: ['long'],
searchable: true,
aggregatable: true,
name: 'foo',
readFromDocValues: true,
metadata_field: false,
},
{
aggregatable: true,
esTypes: ['keyword'],
name: 'nestedField.child',
readFromDocValues: true,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
metadata_field: false,
},
],
indices: ['basic_index'],
})
.then(ensureFieldsAreSorted);
});
it('returns fields when one pattern exists and the other does not', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'bad_index,basic_index' })
.expect(200, {
fields: testFields,
indices: ['basic_index'],
});
});
it('returns 404 when neither exists', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({ pattern: 'bad_index,bad_index_2' })
.expect(404);
});
it('returns 404 when no patterns exist', async () => {
await supertest
.get(FIELDS_FOR_WILDCARD_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.query({
pattern: 'bad_index',
})
.expect(404);
});
});
}

View file

@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import {
INITIAL_REST_VERSION,
INITIAL_REST_VERSION_INTERNAL,
} from '@kbn/data-views-plugin/server/constants';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
import { configArray } from '../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const es = getService('es');
const svlCommonApi = getService('svlCommonApi');
const kibanaServer = getService('kibanaServer');
describe('has user index pattern API', () => {
configArray.forEach((config) => {
describe(config.name, () => {
beforeEach(async () => {
// TODO: emptyKibanaIndex fails in Serverless with
// "index_not_found_exception: no such index [.kibana_ingest]",
// so it was switched to `savedObjects.cleanStandardList()`
await kibanaServer.savedObjects.cleanStandardList();
if (await es.indices.exists({ index: 'metrics-test' })) {
await es.indices.delete({ index: 'metrics-test' });
}
if (await es.indices.exists({ index: 'logs-test' })) {
await es.indices.delete({ index: 'logs-test' });
}
});
const servicePath = `${config.basePath}/has_user_${config.serviceKey}`;
it('should return false if no index patterns', async () => {
// Make sure all saved objects including data views are cleared
// TODO: emptyKibanaIndex fails in Serverless with
// "index_not_found_exception: no such index [.kibana_ingest]",
// so it was switched to `savedObjects.cleanStandardList()`
await kibanaServer.savedObjects.cleanStandardList();
const response = await supertest
.get(servicePath)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(200);
expect(response.body.result).to.be(false);
});
it('should return true if has index pattern with user data', async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
await supertest
.post(config.path)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title: 'basic_index',
},
});
const response = await supertest
.get(servicePath)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(200);
expect(response.body.result).to.be(true);
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('should return true if has user index pattern without data', async () => {
await supertest
.post(config.path)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title: 'basic_index',
allowNoIndex: true,
},
});
const response = await supertest
.get(servicePath)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(200);
expect(response.body.result).to.be(true);
});
});
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('has user index pattern', () => {
loadTestFile(require.resolve('./has_user_index_pattern'));
});
}

View file

@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('index_patterns', () => {
loadTestFile(require.resolve('./es_errors'));
loadTestFile(require.resolve('./fields_for_wildcard_route'));
loadTestFile(require.resolve('./data_views_crud'));
// TODO: Removed `scripted_fields_crud` since
// scripted fields are not supported in Serverless
loadTestFile(require.resolve('./fields_api'));
loadTestFile(require.resolve('./default_index_pattern'));
loadTestFile(require.resolve('./runtime_fields_crud'));
loadTestFile(require.resolve('./integration'));
// TODO: Removed `deprecations` since
// scripted fields are not supported in Serverless
loadTestFile(require.resolve('./has_user_index_pattern'));
loadTestFile(require.resolve('./swap_references'));
});
}

View file

@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
/**
* Test usage of different index patterns APIs in combination
*/
export default function ({ loadTestFile }: FtrProviderContext) {
describe('integration', () => {
loadTestFile(require.resolve('./integration'));
});
}

View file

@ -0,0 +1,134 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import _ from 'lodash';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
/**
* Test usage of different index patterns APIs in combination
*/
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('integration', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
it('create an index pattern, add a runtime field, add a field formatter, then re-create the same index pattern', async () => {
const title = `basic_index*`;
const response1 = await supertest
.post('/api/index_patterns/index_pattern')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
index_pattern: {
title,
},
});
const id = response1.body.index_pattern.id;
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
runtimeBar: {
count: 123,
customLabel: 'test',
},
},
});
expect(response3.status).to.be(200);
const response4 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fields: {
runtimeBar: {
format: {
id: 'duration',
params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' },
},
},
},
});
expect(response4.status).to.be(200);
const response5 = await supertest
.get('/api/index_patterns/index_pattern/' + response1.body.index_pattern.id)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response5.status).to.be(200);
const resultIndexPattern = response5.body.index_pattern;
const runtimeField = resultIndexPattern.fields.runtimeBar;
expect(runtimeField.name).to.be('runtimeBar');
expect(runtimeField.runtimeField.type).to.be('long');
expect(runtimeField.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
expect(runtimeField.scripted).to.be(false);
expect(resultIndexPattern.fieldFormats.runtimeBar.id).to.be('duration');
expect(resultIndexPattern.fieldFormats.runtimeBar.params.inputFormat).to.be('milliseconds');
expect(resultIndexPattern.fieldFormats.runtimeBar.params.outputFormat).to.be(
'humanizePrecise'
);
expect(resultIndexPattern.fieldAttrs.runtimeBar.count).to.be(123);
expect(resultIndexPattern.fieldAttrs.runtimeBar.customLabel).to.be('test');
// check that retrieved object is transient and a clone can be created
const response6 = await supertest
.post('/api/index_patterns/index_pattern')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
index_pattern: resultIndexPattern,
});
expect(response6.status).to.be(200);
const recreatedIndexPattern = response6.body.index_pattern;
expect(_.omit(recreatedIndexPattern, 'version', 'namespaces')).to.eql(
_.omit(resultIndexPattern, 'version', 'namespaces')
);
});
});
}

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns an error field object is not provided', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title,
},
});
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({});
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be(
'[request body.name]: expected value of type [string] but got [undefined]'
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('create_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,229 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('can create a new runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
},
});
const id = response1.body[config.serviceKey].id;
const response2 = await supertest
.post(`${config.path}/${id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey]).to.not.be.empty();
const field =
config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0];
expect(field.name).to.be('runtimeBar');
expect(field.runtimeField.type).to.be('long');
expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
expect(field.scripted).to.be(false);
});
it('newly created runtime field is available in the index_pattern object', async () => {
const title = `basic_index`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
},
});
await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey]).to.not.be.empty();
const field = response2.body[config.serviceKey].fields.runtimeBar;
expect(field.name).to.be('runtimeBar');
expect(field.runtimeField.type).to.be('long');
expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
expect(field.scripted).to.be(false);
const response3 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response3.status).to.be(400);
await supertest
.delete(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
});
it('prevents field name collisions', async () => {
const title = `basic*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response3.status).to.be(400);
const response4 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeComposite',
runtimeField: {
type: 'composite',
script: {
source: 'emit("a","a"); emit("b","b")',
},
fields: {
a: {
type: 'keyword',
},
b: {
type: 'keyword',
},
},
},
});
expect(response4.status).to.be(200);
const response5 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeComposite',
runtimeField: {
type: 'composite',
script: {
source: 'emit("a","a"); emit("b","b")',
},
fields: {
a: {
type: 'keyword',
},
b: {
type: 'keyword',
},
},
},
});
expect(response5.status).to.be(400);
});
});
});
});
}

View file

@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
const basicIndex = 'b*sic_index';
let indexPattern: any;
configArray.forEach((config) => {
describe(config.name, () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
indexPattern = (
await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title: basicIndex,
},
})
).body[config.serviceKey];
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
if (indexPattern) {
await supertest
.delete(`${config.path}/${indexPattern.id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.delete(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(404);
});
it('returns 404 error on non-existing runtime field', async () => {
const response1 = await supertest
.delete(`${config.path}/${indexPattern.id}/runtime_field/test`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response1.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest
.delete(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('delete_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,81 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('can delete a runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeBar: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
},
},
});
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(typeof response2.body[config.serviceKey].fields.runtimeBar).to.be('object');
const response3 = await supertest
.delete(
`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`
)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response3.status).to.be(200);
const response4 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(typeof response4.body[config.serviceKey].fields.runtimeBar).to.be('undefined');
await supertest
.delete(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
});
});
});
});
}

View file

@ -0,0 +1,88 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
const basicIndex = '*asic_index';
let indexPattern: any;
configArray.forEach((config) => {
describe(config.name, () => {
before(async () => {
await esArchiver.load(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
indexPattern = (
await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
[config.serviceKey]: {
title: basicIndex,
},
})
).body[config.serviceKey];
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
if (indexPattern) {
await supertest
.delete(`${config.path}/${indexPattern.id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.get(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(404);
});
it('returns 404 error on non-existing runtime field', async () => {
const response1 = await supertest
.get(`${config.path}/${indexPattern.id}/runtime_field/sf`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response1.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest
.get(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('get_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('can fetch a runtime field', async () => {
const title = `basic_index*`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "emit(doc['field_name'].value)",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "emit(doc['field_name'].value)",
},
},
},
},
});
expect(response1.status).to.be(200);
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const field =
config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0];
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey]).to.not.be.empty();
expect(typeof field).to.be('object');
expect(field.name).to.be('runtimeFoo');
expect(field.type).to.be('string');
expect(field.scripted).to.be(false);
expect(field.runtimeField.script.source).to.be("emit(doc['field_name'].value)");
await supertest
.delete(`${config.path}/${response1.body[config.serviceKey].id}`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
});
});
});
});
}

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('runtime_fields_crud', () => {
loadTestFile(require.resolve('./create_runtime_field'));
loadTestFile(require.resolve('./get_runtime_field'));
loadTestFile(require.resolve('./delete_runtime_field'));
loadTestFile(require.resolve('./put_runtime_field'));
loadTestFile(require.resolve('./update_runtime_field'));
});
}

View file

@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.put(`${config.path}/${id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response.status).to.be(404);
});
it('returns error on non-runtime field update attempt', async () => {
const title = `basic_index`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
},
});
const response2 = await supertest
.put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'bar',
runtimeField: {
type: 'long',
script: {
source: "emit(doc['field_name'].value)",
},
},
});
expect(response2.status).to.be(400);
expect(response2.body.message).to.be('Only runtime fields can be updated');
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('put_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,147 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('can overwrite an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
const response2 = await supertest
.put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeFoo',
runtimeField: {
type: 'long',
script: {
source: "doc['field_name'].value",
},
},
});
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey]).to.not.be.empty();
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const field3 =
config.serviceKey === 'index_pattern' ? response3.body.field : response3.body.fields[0];
expect(response3.status).to.be(200);
expect(field3.type).to.be('number');
const response4 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const field4 =
config.serviceKey === 'index_pattern' ? response4.body.field : response4.body.fields[0];
expect(response4.status).to.be(200);
expect(field4.type).to.be('string');
});
it('can add a new runtime field', async () => {
const title = `basic_index`;
const response1 = await supertest
.post(config.path)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
await supertest
.put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'runtimeBar',
runtimeField: {
type: 'long',
script: {
source: "doc['field_name'].value",
},
},
});
const response2 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const field =
config.serviceKey === 'index_pattern' ? response2.body.field : response2.body.fields[0];
expect(response2.status).to.be(200);
expect(response2.body[config.serviceKey]).to.not.be.empty();
expect(typeof field.runtimeField).to.be('object');
});
});
});
});
}

View file

@ -0,0 +1,62 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
configArray.forEach((config) => {
describe(config.name, () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.post(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
runtimeField: {
type: 'keyword',
script: {
source: "doc['something_new'].value",
},
},
});
expect(response.status).to.be(404);
});
it('returns error when field name is specified', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.post(`${config.path}/${id}/runtime_field/foo`)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
name: 'foo',
runtimeField: {
type: 'keyword',
script: {
source: "doc['something_new'].value",
},
},
});
expect(response.status).to.be(400);
expect(response.body.statusCode).to.be(400);
expect(response.body.message).to.be(
"[request body.name]: a value wasn't expected to be present"
);
});
});
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('update_runtime_field', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,114 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../../../ftr_provider_context';
import { configArray } from '../../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('main', () => {
before(async () => {
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
});
after(async () => {
await esArchiver.unload(
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
);
});
configArray.forEach((config) => {
describe(config.name, () => {
it('can update an existing field', async () => {
const title = `basic_index`;
const response1 = await supertest
.post(config.path)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
override: true,
[config.serviceKey]: {
title,
runtimeFieldMap: {
runtimeFoo: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
runtimeBar: {
type: 'keyword',
script: {
source: "doc['field_name'].value",
},
},
},
},
});
const response2 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
runtimeField: {
type: 'keyword',
script: {
source: "doc['something_new'].value",
},
},
});
expect(response2.status).to.be(200);
const response3 = await supertest
.get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
const field =
config.serviceKey === 'index_pattern' ? response3.body.field : response3.body.fields[0];
expect(response3.status).to.be(200);
expect(response3.body[config.serviceKey]).to.not.be.empty();
expect(field.type).to.be('string');
expect(field.runtimeField.type).to.be('keyword');
expect(field.runtimeField.script.source).to.be("doc['something_new'].value");
// Partial update
const response4 = await supertest
.post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
runtimeField: {
script: {
source: "doc['partial_update'].value",
},
},
});
expect(response4.status).to.be(200);
const field2 =
config.serviceKey === 'index_pattern' ? response4.body.field : response4.body.fields[0];
expect(field2.runtimeField.script.source).to.be("doc['partial_update'].value");
});
});
});
});
}

View file

@ -0,0 +1,44 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
import { dataViewConfig } from '../constants';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('errors', () => {
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest
.get(`${dataViewConfig.path}/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(404);
});
it('returns error when ID is too long', async () => {
const id = `xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx`;
const response = await supertest
.get(`${dataViewConfig.path}/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(response.status).to.be(400);
expect(response.body.message).to.be(
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
});
}

View file

@ -0,0 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('swap_references', () => {
loadTestFile(require.resolve('./errors'));
loadTestFile(require.resolve('./main'));
});
}

View file

@ -0,0 +1,212 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import {
DATA_VIEW_SWAP_REFERENCES_PATH,
SPECIFIC_DATA_VIEW_PATH,
DATA_VIEW_PATH,
} from '@kbn/data-views-plugin/server';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { INITIAL_REST_VERSION } from '@kbn/data-views-plugin/server/constants';
import type { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
const title = 'logs-*';
const prevDataViewId = '91200a00-9efd-11e7-acb3-3dab96693fab';
const PREVIEW_PATH = `${DATA_VIEW_SWAP_REFERENCES_PATH}/_preview`;
let dataViewId = '';
describe('main', () => {
const kibanaServer = getService('kibanaServer');
before(async () => {
const result = await supertest
.post(DATA_VIEW_PATH)
.send({ data_view: { title } })
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
dataViewId = result.body.data_view.id;
});
after(async () => {
await supertest
.delete(SPECIFIC_DATA_VIEW_PATH.replace('{id}', dataViewId))
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
});
beforeEach(async () => {
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
afterEach(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
it('can preview', async () => {
const res = await supertest
.post(PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fromId: prevDataViewId,
toId: dataViewId,
});
expect(res).to.have.property('status', 200);
});
it('can preview specifying type', async () => {
const res = await supertest
.post(PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fromId: prevDataViewId,
fromType: 'index-pattern',
toId: dataViewId,
});
expect(res).to.have.property('status', 200);
});
it('can save changes', async () => {
const res = await supertest
.post(DATA_VIEW_SWAP_REFERENCES_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fromId: prevDataViewId,
toId: dataViewId,
});
expect(res).to.have.property('status', 200);
expect(res.body.result.length).to.equal(1);
expect(res.body.result[0].id).to.equal('dd7caf20-9efd-11e7-acb3-3dab96693fab');
expect(res.body.result[0].type).to.equal('visualization');
});
it('can save changes and remove old saved object', async () => {
const res = await supertest
.post(DATA_VIEW_SWAP_REFERENCES_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fromId: prevDataViewId,
toId: dataViewId,
delete: true,
});
expect(res).to.have.property('status', 200);
expect(res.body.result.length).to.equal(1);
expect(res.body.deleteStatus.remainingRefs).to.equal(0);
expect(res.body.deleteStatus.deletePerformed).to.equal(true);
const res2 = await supertest
.get(SPECIFIC_DATA_VIEW_PATH.replace('{id}', prevDataViewId))
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(res2).to.have.property('statusCode', 404);
});
describe('limit affected saved objects', () => {
beforeEach(async () => {
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json'
);
});
afterEach(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json'
);
});
it("won't delete if reference remains", async () => {
const res = await supertest
.post(DATA_VIEW_SWAP_REFERENCES_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
fromId: '8963ca30-3224-11e8-a572-ffca06da1357',
toId: '91200a00-9efd-11e7-acb3-3dab96693fab',
forId: ['960372e0-3224-11e8-a572-ffca06da1357'],
delete: true,
});
expect(res).to.have.property('status', 200);
expect(res.body.result.length).to.equal(1);
expect(res.body.deleteStatus.remainingRefs).to.equal(1);
expect(res.body.deleteStatus.deletePerformed).to.equal(false);
});
it('can limit by id', async () => {
// confirm this will find two items
const res = await supertest
.post(PREVIEW_PATH)
.send({
fromId: '8963ca30-3224-11e8-a572-ffca06da1357',
toId: '91200a00-9efd-11e7-acb3-3dab96693fab',
})
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(res).to.have.property('status', 200);
expect(res.body.result.length).to.equal(2);
// limit to one item
const res2 = await supertest
.post(DATA_VIEW_SWAP_REFERENCES_PATH)
.send({
fromId: '8963ca30-3224-11e8-a572-ffca06da1357',
toId: '91200a00-9efd-11e7-acb3-3dab96693fab',
forId: ['960372e0-3224-11e8-a572-ffca06da1357'],
})
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(res2).to.have.property('status', 200);
expect(res2.body.result.length).to.equal(1);
});
it('can limit by type', async () => {
// confirm this will find two items
const res = await supertest
.post(PREVIEW_PATH)
.send({
fromId: '8963ca30-3224-11e8-a572-ffca06da1357',
toId: '91200a00-9efd-11e7-acb3-3dab96693fab',
})
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(res).to.have.property('status', 200);
expect(res.body.result.length).to.equal(2);
// limit to one item
const res2 = await supertest
.post(DATA_VIEW_SWAP_REFERENCES_PATH)
.send({
fromId: '8963ca30-3224-11e8-a572-ffca06da1357',
toId: '91200a00-9efd-11e7-acb3-3dab96693fab',
forType: 'search',
})
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader());
expect(res2).to.have.property('status', 200);
expect(res2.body.result.length).to.equal(1);
});
});
});
}

View file

@ -28,5 +28,11 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./index_management'));
loadTestFile(require.resolve('./alerting'));
loadTestFile(require.resolve('./ingest_pipelines'));
loadTestFile(require.resolve('./data_view_field_editor'));
loadTestFile(require.resolve('./data_views'));
loadTestFile(require.resolve('./kql_telemetry'));
loadTestFile(require.resolve('./scripts_tests'));
loadTestFile(require.resolve('./search_oss'));
loadTestFile(require.resolve('./search_xpack'));
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('KQL', () => {
loadTestFile(require.resolve('./kql_telemetry'));
});
}

View file

@ -0,0 +1,156 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { get } from 'lodash';
import { ANALYTICS_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server';
import { KQL_TELEMETRY_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const kibanaServer = getService('kibanaServer');
const es = getService('es');
const svlCommonApi = getService('svlCommonApi');
describe('telemetry API', () => {
before(async () => {
// TODO: Clean `kql-telemetry` before running the tests
await kibanaServer.savedObjects.clean({ types: ['kql-telemetry'] });
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
it('should increment the opt *in* counter in the .kibana_analytics/kql-telemetry document', async () => {
await supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: true })
.expect(200);
return es
.search({
index: ANALYTICS_SAVED_OBJECT_INDEX,
q: 'type:kql-telemetry',
})
.then((response) => {
const kqlTelemetryDoc = get(response, 'hits.hits[0]._source.kql-telemetry');
expect(kqlTelemetryDoc.optInCount).to.be(1);
});
});
it('should increment the opt *out* counter in the .kibana_analytics/kql-telemetry document', async () => {
await supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: false })
.expect(200);
return es
.search({
index: ANALYTICS_SAVED_OBJECT_INDEX,
q: 'type:kql-telemetry',
})
.then((response) => {
const kqlTelemetryDoc = get(response, 'hits.hits[0]._source.kql-telemetry');
expect(kqlTelemetryDoc.optOutCount).to.be(1);
});
});
it('should report success when opt *in* is incremented successfully', () => {
return (
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: true })
.expect('Content-Type', /json/)
.expect(200)
.then(({ body }) => {
expect(body.success).to.be(true);
})
);
});
it('should report success when opt *out* is incremented successfully', () => {
return (
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: false })
.expect('Content-Type', /json/)
.expect(200)
.then(({ body }) => {
expect(body.success).to.be(true);
})
);
});
it('should only accept literal boolean values for the opt_in POST body param', function () {
return Promise.all([
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: 'notabool' })
.expect(400),
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: 0 })
.expect(400),
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: null })
.expect(400),
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({ opt_in: undefined })
.expect(400),
supertest
.post('/internal/kql_opt_in_stats')
.set('content-type', 'application/json')
.set(ELASTIC_HTTP_VERSION_HEADER, KQL_TELEMETRY_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({})
.expect(400),
]);
});
});
}

View 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
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export default function ({ loadTestFile }) {
// TODO: The `scripts` folder was renamed to `scripts_tests` because the folder
// name `scripts` triggers the `eslint@kbn/imports/no_boundary_crossing` rule
describe('scripts', () => {
loadTestFile(require.resolve('./languages'));
});
}

View file

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION } from '@kbn/data-plugin/common/constants';
export default function ({ getService }) {
const supertest = getService('supertest');
const svlCommonApi = getService('svlCommonApi');
describe('Script Languages API', function getLanguages() {
it('should return 200 with an array of languages', () =>
supertest
.get('/internal/scripts/languages')
.set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response) => {
expect(response.body).to.be.an('array');
}));
// eslint-disable-next-line jest/no-disabled-tests
it.skip('should only return langs enabled for inline scripting', () =>
supertest
.get('/internal/scripts/languages')
.set(ELASTIC_HTTP_VERSION_HEADER, SCRIPT_LANGUAGES_ROUTE_LATEST_VERSION)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response) => {
expect(response.body).to.contain('expression');
expect(response.body).to.contain('painless');
expect(response.body).to.not.contain('groovy');
}));
});
}

View file

@ -0,0 +1,249 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import request from 'superagent';
import { inflateResponse } from '@kbn/bfetch-plugin/public/streaming';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { BFETCH_ROUTE_VERSION_LATEST } from '@kbn/bfetch-plugin/common';
import type { FtrProviderContext } from '../../../ftr_provider_context';
import { painlessErrReq } from './painless_err_req';
import { verifyErrorResponse } from './verify_error';
function parseBfetchResponse(resp: request.Response, compressed: boolean = false) {
return resp.text
.trim()
.split('\n')
.map((item) => {
return JSON.parse(compressed ? inflateResponse<any>(item) : item);
});
}
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
describe('bsearch', () => {
describe('post', () => {
it('should return 200 a single response', async () => {
const resp = await supertest
.post(`/internal/bsearch`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: {
params: {
index: '.kibana',
body: {
query: {
match_all: {},
},
},
},
},
options: {
strategy: 'es',
},
},
],
});
const jsonBody = parseBfetchResponse(resp);
expect(resp.status).to.be(200);
expect(jsonBody[0].id).to.be(0);
expect(jsonBody[0].result.isPartial).to.be(false);
expect(jsonBody[0].result.isRunning).to.be(false);
expect(jsonBody[0].result).to.have.property('rawResponse');
});
it('should return 200 a single response from compressed', async () => {
const resp = await supertest
.post(`/internal/bsearch?compress=true`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: {
params: {
index: '.kibana',
body: {
query: {
match_all: {},
},
},
},
},
options: {
strategy: 'es',
},
},
],
});
const jsonBody = parseBfetchResponse(resp, true);
expect(resp.status).to.be(200);
expect(jsonBody[0].id).to.be(0);
expect(jsonBody[0].result.isPartial).to.be(false);
expect(jsonBody[0].result.isRunning).to.be(false);
expect(jsonBody[0].result).to.have.property('rawResponse');
});
it('should return a batch of successful responses', async () => {
const resp = await supertest
.post(`/internal/bsearch`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: {
params: {
index: '.kibana',
body: {
query: {
match_all: {},
},
},
},
},
},
{
request: {
params: {
index: '.kibana',
body: {
query: {
match_all: {},
},
},
},
},
},
],
});
expect(resp.status).to.be(200);
const parsedResponse = parseBfetchResponse(resp);
expect(parsedResponse).to.have.length(2);
parsedResponse.forEach((responseJson) => {
expect(responseJson.result).to.have.property('isPartial');
expect(responseJson.result).to.have.property('isRunning');
expect(responseJson.result).to.have.property('rawResponse');
});
});
it('should return error for not found strategy', async () => {
const resp = await supertest
.post(`/internal/bsearch`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: {
params: {
index: '.kibana',
body: {
query: {
match_all: {},
},
},
},
},
options: {
strategy: 'wtf',
},
},
],
});
expect(resp.status).to.be(200);
parseBfetchResponse(resp).forEach((responseJson, i) => {
expect(responseJson.id).to.be(i);
verifyErrorResponse(responseJson.error, 404, 'Search strategy wtf not found');
});
});
it('should return 400 when index type is provided in "es" strategy', async () => {
const resp = await supertest
.post(`/internal/bsearch`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: {
index: '.kibana',
indexType: 'baad',
params: {
body: {
query: {
match_all: {},
},
},
},
},
options: {
strategy: 'es',
},
},
],
});
expect(resp.status).to.be(200);
parseBfetchResponse(resp).forEach((responseJson, i) => {
expect(responseJson.id).to.be(i);
verifyErrorResponse(responseJson.error, 400, 'Unsupported index pattern type baad');
});
});
describe('painless', () => {
before(async () => {
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
});
after(async () => {
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
});
it('should return 400 "search_phase_execution_exception" for Painless error in "es" strategy', async () => {
const resp = await supertest
.post(`/internal/bsearch`)
.set(ELASTIC_HTTP_VERSION_HEADER, BFETCH_ROUTE_VERSION_LATEST)
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
batch: [
{
request: painlessErrReq,
options: {
strategy: 'es',
},
},
],
});
expect(resp.status).to.be(200);
parseBfetchResponse(resp).forEach((responseJson, i) => {
expect(responseJson.id).to.be(i);
verifyErrorResponse(responseJson.error, 400, 'search_phase_execution_exception', true);
});
});
});
});
});
}

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
// TODO: This `search` folder was renamed to `search_oss` to
// differentiate it from the x-pack `search` folder (now `search_xpack`)
describe('search', () => {
loadTestFile(require.resolve('./search'));
// TODO: Removed `sql_search` since
// SQL is not supported in Serverless
loadTestFile(require.resolve('./bsearch'));
});
}

View file

@ -0,0 +1,43 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export const painlessErrReq = {
params: {
index: 'log*',
body: {
size: 500,
fields: ['*'],
script_fields: {
invalid_scripted_field: {
script: {
source: 'invalid',
lang: 'painless',
},
},
},
stored_fields: ['*'],
query: {
bool: {
filter: [
{
match_all: {},
},
{
range: {
'@timestamp': {
gte: '2015-01-19T12:27:55.047Z',
lte: '2021-01-19T12:27:55.047Z',
format: 'strict_date_optional_time',
},
},
},
],
},
},
},
},
};

View file

@ -0,0 +1,203 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import expect from '@kbn/expect';
import type { FtrProviderContext } from '../../../ftr_provider_context';
import { painlessErrReq } from './painless_err_req';
import { verifyErrorResponse } from './verify_error';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
const kibanaServer = getService('kibanaServer');
describe('search', () => {
before(async () => {
// TODO: emptyKibanaIndex fails in Serverless with
// "index_not_found_exception: no such index [.kibana_ingest]",
// so it was switched to `savedObjects.cleanStandardList()`
await kibanaServer.savedObjects.cleanStandardList();
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
});
after(async () => {
await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional');
});
describe('post', () => {
it('should return 200 when correctly formatted searches are provided', async () => {
const resp = await supertest
.post(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
params: {
body: {
query: {
match_all: {},
},
},
},
})
.expect(200);
expect(resp.status).to.be(200);
expect(resp.body.isPartial).to.be(false);
expect(resp.body.isRunning).to.be(false);
expect(resp.body).to.have.property('rawResponse');
expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1');
});
it('should return 200 if terminated early', async () => {
const resp = await supertest
.post(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
params: {
terminateAfter: 1,
index: 'log*',
size: 1000,
body: {
query: {
match_all: {},
},
},
},
})
.expect(200);
expect(resp.status).to.be(200);
expect(resp.body.isPartial).to.be(false);
expect(resp.body.isRunning).to.be(false);
expect(resp.body.rawResponse.terminated_early).to.be(true);
expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1');
});
it('should return 404 when if no strategy is provided', async () => {
const resp = await supertest
.post(`/internal/search`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
body: {
query: {
match_all: {},
},
},
})
.expect(404);
verifyErrorResponse(resp.body, 404);
});
it('should return 404 when if unknown strategy is provided', async () => {
const resp = await supertest
.post(`/internal/search/banana`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
body: {
query: {
match_all: {},
},
},
})
.expect(404);
verifyErrorResponse(resp.body, 404);
expect(resp.body.message).to.contain('banana not found');
expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1');
});
it('should return 400 with illegal ES argument', async () => {
const resp = await supertest
.post(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
params: {
timeout: 1, // This should be a time range string!
index: 'log*',
size: 1000,
body: {
query: {
match_all: {},
},
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true);
});
it('should return 400 with a bad body', async () => {
const resp = await supertest
.post(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send({
params: {
body: {
index: 'nope nope',
bad_query: [],
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'parsing_exception', true);
});
it('should return 400 for a painless error', async () => {
const resp = await supertest
.post(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send(painlessErrReq)
.expect(400);
verifyErrorResponse(resp.body, 400, 'search_phase_execution_exception', true);
});
});
describe('delete', () => {
it('should return 404 when no search id provided', async () => {
const resp = await supertest
.delete(`/internal/search/es`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send()
.expect(404);
verifyErrorResponse(resp.body, 404);
});
it('should return 400 when trying a delete on a non supporting strategy', async () => {
const resp = await supertest
.delete(`/internal/search/es/123`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.send()
.expect(400);
verifyErrorResponse(resp.body, 400);
expect(resp.body.message).to.contain("Search strategy es doesn't support cancellations");
expect(resp.header).to.have.property(ELASTIC_HTTP_VERSION_HEADER, '1');
});
});
});
}

View file

@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
export const verifyErrorResponse = (
r: any,
expectedCode: number,
message?: string,
shouldHaveAttrs?: boolean
) => {
expect(r.statusCode).to.be(expectedCode);
if (message) {
expect(r.message).to.include.string(message);
}
if (shouldHaveAttrs) {
expect(r).to.have.property('attributes');
expect(r.attributes).to.have.property('root_cause');
} else {
expect(r).not.to.have.property('attributes');
}
};

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
// TODO: This `search` folder was renamed to `search_xpack` to
// differentiate it from the oss `search` folder (now `search_oss`)
describe('search', () => {
loadTestFile(require.resolve('./search'));
// TODO: Removed `session` since search
// sessions are not supported in Serverless
});
}

View file

@ -0,0 +1,501 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { parse as parseCookie } from 'tough-cookie';
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import { omit } from 'lodash';
import type { FtrProviderContext } from '../../../ftr_provider_context';
import { verifyErrorResponse } from '../search_oss/verify_error';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const es = getService('es');
const log = getService('log');
const retry = getService('retry');
const security = getService('security');
// TODO: `supertestWithoutAuth` is typed as `any` in `x-pack/test/api_integration/apis/search/search.ts`,
// but within Serverless tests it's typed as `supertest.SuperTest<supertest.Test>`. This causes TS errors
// when accessing `loginResponse.headers`, so we cast it as `any` here to match the original tests.
const supertestNoAuth = getService('supertestWithoutAuth') as any;
const svlCommonApi = getService('svlCommonApi');
const shardDelayAgg = (delay: string) => ({
aggs: {
delay: {
shard_delay: {
value: delay,
},
},
},
});
async function markRequiresShardDelayAgg(testContext: Mocha.Context) {
const body = await es.info();
if (!body.version.number.includes('SNAPSHOT')) {
log.debug('Skipping because this build does not have the required shard_delay agg');
testContext.skip();
}
}
describe('search', () => {
before(async () => {
// ensure es not empty
await es.index({
index: 'search-api-test',
id: 'search-api-test-doc',
body: { message: 'test doc' },
refresh: 'wait_for',
});
});
after(async () => {
await es.indices.delete({
index: 'search-api-test',
});
});
describe('post', () => {
it('should return 200 with final response without search id if wait_for_completion_timeout is long enough', async function () {
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
},
wait_for_completion_timeout: '10s',
},
})
.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 search id and partial response if wait_for_completion_timeout is not long enough', async function () {
await markRequiresShardDelayAgg(this);
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
...shardDelayAgg('3s'),
},
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 from completed search with search id', async function () {
await markRequiresShardDelayAgg(this);
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
...shardDelayAgg('3s'),
},
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);
await new Promise((resolve) => setTimeout(resolve, 3000));
await retry.tryForTime(10000, async () => {
const resp2 = await supertest
.post(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.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);
return true;
});
});
it('should retrieve results from in-progress search with search id', async function () {
await markRequiresShardDelayAgg(this);
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
...shardDelayAgg('10s'),
},
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);
const resp2 = await supertest
.post(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({})
.expect(200);
expect(resp2.body.id).not.to.be(undefined);
expect(resp2.body.isPartial).to.be(true);
expect(resp2.body.isRunning).to.be(true);
});
it('should fail without kbn-xref header', async () => {
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(omit(svlCommonApi.getInternalRequestHeader(), 'kbn-xsrf'))
.send({
params: {
body: {
query: {
match_all: {},
},
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'Request must contain a kbn-xsrf header.');
});
it('should return 400 when unknown index type is provided', async () => {
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
indexType: 'baad',
params: {
body: {
query: {
match_all: {},
},
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'Unknown indexType');
});
it('should return 400 if invalid id is provided', async () => {
const resp = await supertest
.post(`/internal/search/ese/123`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true);
});
it('should return 404 if unknown id is provided', async () => {
const resp = await supertest
.post(
`/internal/search/ese/FkxOb21iV1g2VGR1S2QzaWVtRU9fMVEbc3JWeWc1VHlUdDZ6MENxcXlYVG1Fdzo2NDg4`
)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
},
},
})
.expect(404);
verifyErrorResponse(resp.body, 404, 'resource_not_found_exception', true);
});
it('should return 400 with a bad body', async () => {
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
index: 'nope nope',
bad_query: [],
},
},
})
.expect(400);
verifyErrorResponse(resp.body, 400, 'parsing_exception', true);
});
// TODO: Security works differently in Serverless so this test fails,
// we'll need to figure out how to test this properly in Serverless
it.skip('should return 403 for lack of privledges', async () => {
const username = 'no_access';
const password = 't0pS3cr3t';
await security.user.create(username, {
password,
roles: ['test_shakespeare_reader'],
});
const loginResponse = await supertestNoAuth
.post('/internal/security/login')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'xxx')
.send({
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: { username, password },
})
.expect(200);
const sessionCookie = parseCookie(loginResponse.headers['set-cookie'][0]);
await supertestNoAuth
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.set('Cookie', sessionCookie!.cookieString())
.send({
params: {
index: 'log*',
body: {
query: {
match_all: {},
},
},
wait_for_completion_timeout: '10s',
},
})
.expect(403);
await security.testUser.restoreDefaults();
});
});
// TODO: Removed rollup tests since rollups aren't supported in Serverless
describe('delete', () => {
it('should return 404 when no search id provided', async () => {
await supertest
.delete(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.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(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send()
.expect(400);
verifyErrorResponse(resp.body, 400, 'illegal_argument_exception', true);
});
it('should delete an in-progress search', async function () {
await markRequiresShardDelayAgg(this);
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
...shardDelayAgg('10s'),
},
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);
await supertest
.delete(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send()
.expect(200);
// try to re-fetch
await supertest
.post(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({})
.expect(404);
});
// FLAKY: https://github.com/elastic/kibana/issues/164856
it.skip('should delete a completed search', async function () {
await markRequiresShardDelayAgg(this);
const resp = await supertest
.post(`/internal/search/ese`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({
params: {
body: {
query: {
match_all: {},
},
...shardDelayAgg('3s'),
},
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);
await new Promise((resolve) => setTimeout(resolve, 3000));
await retry.tryForTime(10000, async () => {
const resp2 = await supertest
.post(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.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);
return true;
});
await supertest
.delete(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send()
.expect(200);
// try to re-fetch
await supertest
.post(`/internal/search/ese/${id}`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
// TODO: API requests in Serverless require internal request headers
.set(svlCommonApi.getInternalRequestHeader())
.set('kbn-xsrf', 'foo')
.send({})
.expect(404);
});
});
});
}

View file

@ -48,5 +48,8 @@
"@kbn/data-views-plugin",
"@kbn/core-saved-objects-server",
"@kbn/security-api-integration-helpers",
"@kbn/data-view-field-editor-plugin",
"@kbn/data-plugin",
"@kbn/bfetch-plugin",
]
}