diff --git a/src/platform/plugins/shared/data_views/server/fetcher/lib/errors.test.ts b/src/platform/plugins/shared/data_views/server/fetcher/lib/errors.test.ts new file mode 100644 index 0000000000000..2c03ba43c1a25 --- /dev/null +++ b/src/platform/plugins/shared/data_views/server/fetcher/lib/errors.test.ts @@ -0,0 +1,133 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import Boom from '@hapi/boom'; +import { errors as esErrors } from '@elastic/elasticsearch'; + +import { + isEsIndexNotFoundError, + createNoMatchingIndicesError, + isNoMatchingIndicesError, + convertEsError, +} from './errors'; + +// Mirrors the live ES "index_not_found_exception" payload shape so the +// behaviour stays in sync with what `@elastic/elasticsearch` throws. +const buildIndexNotFoundError = () => + new esErrors.ResponseError({ + statusCode: 404, + body: { + error: { + type: 'index_not_found_exception', + reason: 'no such index [SHOULD NOT EXIST]', + index: 'SHOULD NOT EXIST', + 'resource.id': 'SHOULD NOT EXIST', + 'resource.type': 'index_or_alias', + }, + }, + } as ConstructorParameters[0]); + +const buildDocNotFoundError = () => + new esErrors.ResponseError({ + statusCode: 404, + body: { + _index: 'basic_index', + _id: '1234', + found: false, + }, + } as ConstructorParameters[0]); + +describe('index_patterns/* error handler', () => { + describe('isEsIndexNotFoundError()', () => { + it('identifies index not found errors', () => { + expect(isEsIndexNotFoundError(buildIndexNotFoundError())).toBe(true); + }); + + it('rejects doc not found errors', () => { + expect(isEsIndexNotFoundError(buildDocNotFoundError())).toBe(false); + }); + }); + + describe('createNoMatchingIndicesError()', () => { + it('returns a boom error', () => { + const error = createNoMatchingIndicesError('foo*') as Boom.Boom; + expect(error.isBoom).toBe(true); + }); + + it('sets output code to "no_matching_indices"', () => { + const error = createNoMatchingIndicesError('foo*') as Boom.Boom; + expect(error.output.payload).toHaveProperty('code', 'no_matching_indices'); + }); + }); + + describe('isNoMatchingIndicesError()', () => { + it('returns true for errors from createNoMatchingIndicesError()', () => { + expect(isNoMatchingIndicesError(createNoMatchingIndicesError('foo*'))).toBe(true); + }); + + it('returns false for indexNotFoundError', () => { + expect(isNoMatchingIndicesError(buildIndexNotFoundError())).toBe(false); + }); + + it('returns false for docNotFoundError', () => { + expect(isNoMatchingIndicesError(buildDocNotFoundError())).toBe(false); + }); + }); + + describe('convertEsError()', () => { + const indices = ['foo', 'bar']; + + it('converts indexNotFoundErrors into NoMatchingIndices errors', () => { + const converted = convertEsError(indices, buildIndexNotFoundError()); + expect(isNoMatchingIndicesError(converted)).toBe(true); + }); + + it('wraps other errors in Boom', () => { + const error = new esErrors.ResponseError({ + statusCode: 403, + 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]', + }, + } as ConstructorParameters[0]); + + expect(error).not.toHaveProperty('isBoom'); + const converted = convertEsError(indices, error) as Boom.Boom; + expect(converted).toHaveProperty('isBoom'); + expect(converted.output.statusCode).toBe(403); + }); + + it('handles errors that are already Boom errors', () => { + const error = new Error() as Error & { statusCode?: number }; + error.statusCode = 401; + const boomError = Boom.boomify(error, { statusCode: error.statusCode }); + + const converted = convertEsError(indices, boomError) as Boom.Boom; + + expect(converted.output.statusCode).toBe(401); + }); + + it('preserves headers from Boom errors', () => { + const error = new Error() as Error & { statusCode?: number }; + 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) as Boom.Boom; + + expect(converted.output.headers['WWW-Authenticate']).toBe(wwwAuthenticate); + }); + }); +}); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts b/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts index 8c9f73b80f2ce..5af448820b175 100644 --- a/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts +++ b/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts @@ -21,12 +21,23 @@ export const COMMON_HEADERS = { export const ES_ARCHIVE_BASIC_INDEX = 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index'; +export const ES_ARCHIVE_CONFLICTS = + 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/conflicts'; + +export const KBN_ARCHIVE_SAVED_OBJECTS_BASIC = + 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'; + +export const KBN_ARCHIVE_SAVED_OBJECTS_RELATIONSHIPS = + 'src/platform/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json'; + export const DATA_VIEW_PATH_LEGACY = 'api/index_patterns/index_pattern'; export const DATA_VIEW_PATH = 'api/data_views/data_view'; export const SERVICE_PATH_LEGACY = 'api/index_patterns'; export const SERVICE_PATH = 'api/data_views'; export const SERVICE_KEY_LEGACY = 'index_pattern'; export const SERVICE_KEY = 'data_view'; +export const HAS_USER_DATA_VIEW_PATH = 'api/data_views/has_user_data_view'; +export const HAS_USER_INDEX_PATTERN_PATH = 'api/index_patterns/has_user_index_pattern'; export const ID_OVER_MAX_LENGTH = 'x'.repeat(1759); @@ -36,4 +47,9 @@ export const INTERNAL_COMMON_HEADERS = { [ELASTIC_HTTP_VERSION_HEADER]: '1', }; +export const EXISTING_INDICES_PATH = 'internal/data_views/_existing_indices'; export const FIELDS_FOR_WILDCARD_PATH = 'internal/data_views/_fields_for_wildcard'; +export const FIELDS_ROUTE_PATH = 'internal/data_views/fields'; +export const RESOLVE_INDEX_PATH = 'internal/index-pattern-management/resolve_index'; +export const SWAP_REFERENCES_PATH = `${SERVICE_PATH}/swap_references`; +export const SWAP_REFERENCES_PREVIEW_PATH = `${SERVICE_PATH}/swap_references/_preview`; diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_errors.spec.ts new file mode 100644 index 0000000000000..a9f96ec028c2c --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_errors.spec.ts @@ -0,0 +1,162 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { COMMON_HEADERS, DATA_VIEW_PATH, SERVICE_KEY } from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH}/{id}/fields - errors (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns 404 error on non-existing data view', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: {}, + }, + }, + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when "fields" payload attribute is invalid', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: 123, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe( + '[request body.fields]: expected value of type [object] but got [number]' + ); + }); + + apiTest('returns error if no changes are specified', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: {}, + bar: {}, + baz: {}, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe('Change set is empty.'); + }); + + apiTest('returns validation error for too long customDescription', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: { + customDescription: 'too long value'.repeat(50), + }, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toContain('it must have a maximum length'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_main.spec.ts new file mode 100644 index 0000000000000..5c789c11e3ac2 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/fields_api_update_main.spec.ts @@ -0,0 +1,352 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH}/{id}/fields - main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + // Shared basic_index-backed data view (has real fields like 'foo') used by tests that + // exercise attribute updates on existing fields. + let sharedDataViewId: string; + + apiTest.beforeAll(async ({ esArchiver, requestAuth, apiClient }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title: 'ba*ic_index' } }, + }); + // Fail fast in setup so suite-level errors don't cascade as confusing assertion errors. + expect(createResponse).toHaveStatusCode(200); + sharedDataViewId = createResponse.body[SERVICE_KEY].id; + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (sharedDataViewId) { + await apiServices.dataViews.delete(sharedDataViewId); + } + }); + + apiTest('can update multiple fields', async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${dataView.id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + foo: { count: 123, customLabel: 'test' }, + bar: { count: 456, customDescription: 'desc' }, + }, + }, + }); + + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(123); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBe('test'); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.bar.count).toBe(456); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.bar.customDescription).toBe('desc'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(123); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.bar.count).toBe(456); + }); + + apiTest( + 'can set field count attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${dataView.id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: 123 } } }, + }); + + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(123); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(123); + } + ); + + apiTest('can update count attribute in attribute map', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title, fieldAttrs: { foo: { count: 1 } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + expect(createResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(1); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: 2 } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(2); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBe(2); + }); + + apiTest('can delete count attribute from attribute map', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title, fieldAttrs: { foo: { count: 1 } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.count).toBeUndefined(); + }); + + apiTest( + 'can set field customLabel attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${dataView.id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'foo' } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBe('foo'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBe('foo'); + } + ); + + apiTest('can update customLabel attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title, fieldAttrs: { foo: { customLabel: 'foo' } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'bar' } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBe('bar'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBe('bar'); + }); + + apiTest('can set field customLabel attribute on an existing field', async ({ apiClient }) => { + await apiClient.post(`${DATA_VIEW_PATH}/${sharedDataViewId}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'baz' } } }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${sharedDataViewId}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY].fields.foo.customLabel).toBe('baz'); + }); + + apiTest('can delete customLabel attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title, fieldAttrs: { foo: { customLabel: 'foo' } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldAttrs.foo.customLabel).toBeUndefined(); + }); + + apiTest( + 'can set field format attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${dataView.id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: { id: 'bar', params: { baz: 'qux' } } } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldFormats.foo).toStrictEqual({ + id: 'bar', + params: { baz: 'qux' }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldFormats.foo).toStrictEqual({ + id: 'bar', + params: { baz: 'qux' }, + }); + } + ); + + apiTest('can update format attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + [SERVICE_KEY]: { + title, + fieldFormats: { foo: { id: 'bar', params: { baz: 'qux' } } }, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: { id: 'bar-2', params: { baz: 'qux-2' } } } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldFormats.foo).toStrictEqual({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldFormats.foo).toStrictEqual({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + }); + + apiTest('can remove format attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + [SERVICE_KEY]: { + title, + fieldFormats: { foo: { id: 'bar', params: { baz: 'qux' } } }, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY].fieldFormats.foo).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY].fieldFormats.foo).toBeUndefined(); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/has_user_data_view.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/has_user_data_view.spec.ts new file mode 100644 index 0000000000000..44915d847cb86 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/has_user_data_view.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + HAS_USER_DATA_VIEW_PATH, + INTERNAL_COMMON_HEADERS, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET ${HAS_USER_DATA_VIEW_PATH} (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + // The "returns false" assertion needs a guaranteed empty starting state. Instead of + // `cleanStandardList()` (a cluster-wide saved-object wipe that can break other suites + // sharing the deployment) we narrowly delete just the existing data views and the two + // test indices the suite reads from. + apiTest.beforeEach(async ({ esClient, apiServices }) => { + const { data: existing } = await apiServices.dataViews.getAll(); + for (const dv of existing) { + await apiServices.dataViews.delete(dv.id); + } + for (const index of ['metrics-test', 'logs-test']) { + if (await esClient.indices.exists({ index })) { + await esClient.indices.delete({ index }); + } + } + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns false if no data views exist', async ({ apiClient }) => { + const response = await apiClient.get(HAS_USER_DATA_VIEW_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(false); + }); + + apiTest('returns true if has data view with user data', async ({ apiClient, esArchiver }) => { + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { title: 'basic_index' }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + createdIds.push(createResponse.body[SERVICE_KEY].id); + + const response = await apiClient.get(HAS_USER_DATA_VIEW_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(true); + }); + + apiTest('returns true if has user data view without data', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title: 'basic_index', + allowNoIndex: true, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + createdIds.push(createResponse.body[SERVICE_KEY].id); + + const response = await apiClient.get(HAS_USER_DATA_VIEW_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(true); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/integration.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/integration.spec.ts new file mode 100644 index 0000000000000..f24d16c0c6d7f --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/integration.spec.ts @@ -0,0 +1,140 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { omit } from 'lodash'; +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe('data views integration (data view api)', { tag: tags.deploymentAgnostic }, () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest( + 'create a data view, add a runtime field, add a field formatter, then re-create the same data view', + async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + // Track immediately so cleanup runs even if the test fails before the recreate step. + createdIds.push(id); + + const runtimeFieldResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/runtime_field`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }); + + expect(runtimeFieldResponse).toHaveStatusCode(200); + + const fieldAttrsResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + runtimeBar: { count: 123, customLabel: 'test' }, + }, + }, + }); + + expect(fieldAttrsResponse).toHaveStatusCode(200); + + const formatterResponse = await apiClient.post(`${DATA_VIEW_PATH}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + runtimeBar: { + format: { + id: 'duration', + params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' }, + }, + }, + }, + }, + }); + + expect(formatterResponse).toHaveStatusCode(200); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + + const resultDataView = getResponse.body[SERVICE_KEY]; + + const runtimeField = resultDataView.fields.runtimeBar; + expect(runtimeField.name).toBe('runtimeBar'); + expect(runtimeField.runtimeField.type).toBe('long'); + expect(runtimeField.runtimeField.script.source).toBe("emit(doc['field_name'].value)"); + expect(runtimeField.scripted).toBe(false); + + expect(resultDataView.fieldFormats.runtimeBar.id).toBe('duration'); + expect(resultDataView.fieldFormats.runtimeBar.params.inputFormat).toBe('milliseconds'); + expect(resultDataView.fieldFormats.runtimeBar.params.outputFormat).toBe('humanizePrecise'); + + expect(resultDataView.fieldAttrs.runtimeBar.count).toBe(123); + expect(resultDataView.fieldAttrs.runtimeBar.customLabel).toBe('test'); + + const recreateResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: resultDataView, + }, + }); + + expect(recreateResponse).toHaveStatusCode(200); + const recreatedDataView = recreateResponse.body[SERVICE_KEY]; + // `override: true` reuses the same id, so the original push above already covers cleanup. + + // The retrieved object should be transient, so a clone re-created from it should + // match the original (ignoring `version`/`namespaces` which are assigned server-side). + expect(omit(recreatedDataView, ['version', 'namespaces'])).toStrictEqual( + omit(resultDataView, ['version', 'namespaces']) + ); + } + ); +}); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_errors.spec.ts new file mode 100644 index 0000000000000..d691f9908386c --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_errors.spec.ts @@ -0,0 +1,85 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { COMMON_HEADERS, DATA_VIEW_PATH, ES_ARCHIVE_BASIC_INDEX } from '../../fixtures/constants'; + +apiTest.describe( + `DELETE ${DATA_VIEW_PATH}/{id}/runtime_field/{name} - errors (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let dataViewId: string; + + apiTest.beforeAll(async ({ esArchiver, requestAuth, apiServices }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const { data: dataView } = await apiServices.dataViews.create({ + title: 'b*sic_index', + override: true, + }); + dataViewId = dataView.id; + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (dataViewId) { + await apiServices.dataViews.delete(dataViewId); + } + }); + + apiTest('returns 404 error on non-existing data view', async ({ apiClient }) => { + const nonExistentId = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + + const response = await apiClient.delete( + `${DATA_VIEW_PATH}/${nonExistentId}/runtime_field/foo`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns 404 error on non-existing runtime field', async ({ apiClient }) => { + const response = await apiClient.delete( + `${DATA_VIEW_PATH}/${dataViewId}/runtime_field/test`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when ID is too long', async ({ apiClient }) => { + const longId = 'x'.repeat(1100); + + const response = await apiClient.delete(`${DATA_VIEW_PATH}/${longId}/runtime_field/foo`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.message).toContain('must have a maximum length of [1000]'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_main.spec.ts new file mode 100644 index 0000000000000..d2a43163e10d4 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_delete_main.spec.ts @@ -0,0 +1,101 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `DELETE ${DATA_VIEW_PATH}/{id}/runtime_field/{name} - main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can delete a runtime field', async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title, + runtimeFieldMap: { + runtimeBar: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(typeof getResponse.body[SERVICE_KEY].fields.runtimeBar).toBe('object'); + + const deleteResponse = await apiClient.delete( + `${DATA_VIEW_PATH}/${id}/runtime_field/runtimeBar`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(deleteResponse).toHaveStatusCode(200); + + const verifyResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(verifyResponse).toHaveStatusCode(200); + expect(verifyResponse.body[SERVICE_KEY].fields.runtimeBar).toBeUndefined(); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_get_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_get_main.spec.ts new file mode 100644 index 0000000000000..227f41b3661f4 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_get_main.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET ${DATA_VIEW_PATH}/{id}/runtime_field/{name} - main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can fetch a runtime field', async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "emit(doc['field_name'].value)" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}/runtime_field/runtimeFoo`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY]).toBeDefined(); + + const field = getResponse.body.fields[0]; + expect(typeof field).toBe('object'); + expect(field.name).toBe('runtimeFoo'); + expect(field.type).toBe('string'); + expect(field.scripted).toBe(false); + expect(field.runtimeField.script.source).toBe("emit(doc['field_name'].value)"); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_errors.spec.ts new file mode 100644 index 0000000000000..99530e30a7442 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_errors.spec.ts @@ -0,0 +1,98 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `PUT ${DATA_VIEW_PATH}/{id}/runtime_field - errors (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns 404 error on non-existing data view', async ({ apiClient }) => { + const nonExistentId = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + + const response = await apiClient.put(`${DATA_VIEW_PATH}/${nonExistentId}/runtime_field`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error on non-runtime field update attempt', async ({ apiClient }) => { + const title = `basic_index`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + // 'bar' is a regular field from basic_index, not a runtime field. + const putResponse = await apiClient.put(`${DATA_VIEW_PATH}/${id}/runtime_field`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + name: 'bar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }); + + expect(putResponse).toHaveStatusCode(400); + expect(putResponse.body.message).toBe('Only runtime fields can be updated'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_main.spec.ts new file mode 100644 index 0000000000000..ab5b52595bbbc --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_put_main.spec.ts @@ -0,0 +1,140 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `PUT ${DATA_VIEW_PATH}/{id}/runtime_field - main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can overwrite an existing field', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + const putResponse = await apiClient.put(`${DATA_VIEW_PATH}/${id}/runtime_field`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeFoo', + runtimeField: { + type: 'long', + script: { source: "doc['field_name'].value" }, + }, + }, + }); + + expect(putResponse).toHaveStatusCode(200); + expect(putResponse.body[SERVICE_KEY]).toBeDefined(); + + const fooResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}/runtime_field/runtimeFoo`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(fooResponse).toHaveStatusCode(200); + // After overwriting, runtimeFoo should now be a number (long) rather than a string (keyword) + expect(fooResponse.body.fields[0].type).toBe('number'); + + const barResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}/runtime_field/runtimeBar`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(barResponse).toHaveStatusCode(200); + // runtimeBar should be unaffected - still keyword (type string) + expect(barResponse.body.fields[0].type).toBe('string'); + }); + + apiTest('can add a new runtime field', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + await apiClient.put(`${DATA_VIEW_PATH}/${id}/runtime_field`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "doc['field_name'].value" }, + }, + }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${id}/runtime_field/runtimeBar`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY]).toBeDefined(); + expect(typeof getResponse.body.fields[0].runtimeField).toBe('object'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_errors.spec.ts new file mode 100644 index 0000000000000..f4003ba618001 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_errors.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { COMMON_HEADERS, DATA_VIEW_PATH } from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH}/{id}/runtime_field/{name} - update errors (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest('returns 404 error on non-existing data view', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/runtime_field/foo`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when field name is specified in body', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + // The update endpoint should not accept a `name` in the body - it comes from the URL. + const response = await apiClient.post(`${DATA_VIEW_PATH}/${id}/runtime_field/foo`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'foo', + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe( + "[request body.name]: a value wasn't expected to be present" + ); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_main.spec.ts new file mode 100644 index 0000000000000..380f2bf071f45 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/runtime_fields_update_main.spec.ts @@ -0,0 +1,120 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH}/{id}/runtime_field/{name} - update main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest( + 'can update an existing runtime field and do a partial update', + async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY].id; + createdIds.push(id); + + await apiTest.step('full update of runtimeFoo', async () => { + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + } + ); + expect(updateResponse).toHaveStatusCode(200); + + const getResponse = await apiClient.get( + `${DATA_VIEW_PATH}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY]).toBeDefined(); + + const field = getResponse.body.fields[0]; + expect(field.type).toBe('string'); + expect(field.runtimeField.type).toBe('keyword'); + expect(field.runtimeField.script.source).toBe("doc['something_new'].value"); + }); + + await apiTest.step('partial update of runtimeFoo (only script)', async () => { + const partialResponse = await apiClient.post( + `${DATA_VIEW_PATH}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + script: { source: "doc['partial_update'].value" }, + }, + }, + } + ); + + expect(partialResponse).toHaveStatusCode(200); + expect(partialResponse.body.fields[0].runtimeField.script.source).toBe( + "doc['partial_update'].value" + ); + }); + } + ); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_errors.spec.ts new file mode 100644 index 0000000000000..04b395cf6f82d --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_errors.spec.ts @@ -0,0 +1,50 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +// Note: the serverless FTR source file lives under `swap_references/errors.ts` but actually +// exercises GET data_view error paths (404 on non-existing id, 400 on oversized id). +// Parity with the original suite is preserved here. + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { COMMON_HEADERS, DATA_VIEW_PATH, ID_OVER_MAX_LENGTH } from '../../fixtures/constants'; + +apiTest.describe( + `GET ${DATA_VIEW_PATH}/{id} - swap_references adjacent errors (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest('returns 404 error on non-existing data view', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await apiClient.get(`${DATA_VIEW_PATH}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when ID is too long', async ({ apiClient }) => { + const response = await apiClient.get(`${DATA_VIEW_PATH}/${ID_OVER_MAX_LENGTH}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.message).toBe( + '[request params.id]: value has length [1759] but it must have a maximum length of [1000].' + ); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_limit.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_limit.spec.ts new file mode 100644 index 0000000000000..8bd298907c649 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_limit.spec.ts @@ -0,0 +1,133 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +// Split from `swap_references/main.ts` (nested `describe('limit affected saved objects')`) +// into its own Scout spec per the one-suite-per-file guideline. + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + KBN_ARCHIVE_SAVED_OBJECTS_BASIC, + KBN_ARCHIVE_SAVED_OBJECTS_RELATIONSHIPS, + SERVICE_KEY, + SWAP_REFERENCES_PATH, + SWAP_REFERENCES_PREVIEW_PATH, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${SWAP_REFERENCES_PATH} - limit affected saved objects (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let dataViewId: string; + + apiTest.beforeAll(async ({ apiClient, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title: 'logs-*' } }, + }); + // Fail fast in setup so suite-level errors don't cascade as confusing assertion errors. + expect(createResponse).toHaveStatusCode(200); + dataViewId = createResponse.body[SERVICE_KEY].id; + }); + + apiTest.beforeEach(async ({ kbnClient }) => { + await kbnClient.importExport.load(KBN_ARCHIVE_SAVED_OBJECTS_BASIC); + await kbnClient.importExport.load(KBN_ARCHIVE_SAVED_OBJECTS_RELATIONSHIPS); + }); + + apiTest.afterEach(async ({ kbnClient }) => { + await kbnClient.importExport.unload(KBN_ARCHIVE_SAVED_OBJECTS_RELATIONSHIPS); + await kbnClient.importExport.unload(KBN_ARCHIVE_SAVED_OBJECTS_BASIC); + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (dataViewId) { + await apiServices.dataViews.delete(dataViewId); + } + }); + + apiTest("won't delete if reference remains", async ({ apiClient }) => { + const response = await apiClient.post(SWAP_REFERENCES_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forId: ['960372e0-3224-11e8-a572-ffca06da1357'], + delete: true, + }, + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toHaveLength(1); + expect(response.body.deleteStatus.remainingRefs).toBe(1); + expect(response.body.deleteStatus.deletePerformed).toBe(false); + }); + + apiTest('can limit by id', async ({ apiClient }) => { + const previewResponse = await apiClient.post(SWAP_REFERENCES_PREVIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + }, + }); + + expect(previewResponse).toHaveStatusCode(200); + expect(previewResponse.body.result).toHaveLength(2); + + const swapResponse = await apiClient.post(SWAP_REFERENCES_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forId: ['960372e0-3224-11e8-a572-ffca06da1357'], + }, + }); + + expect(swapResponse).toHaveStatusCode(200); + expect(swapResponse.body.result).toHaveLength(1); + }); + + apiTest('can limit by type', async ({ apiClient }) => { + const previewResponse = await apiClient.post(SWAP_REFERENCES_PREVIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + }, + }); + + expect(previewResponse).toHaveStatusCode(200); + expect(previewResponse.body.result).toHaveLength(2); + + const swapResponse = await apiClient.post(SWAP_REFERENCES_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: '8963ca30-3224-11e8-a572-ffca06da1357', + toId: '91200a00-9efd-11e7-acb3-3dab96693fab', + forType: 'search', + }, + }); + + expect(swapResponse).toHaveStatusCode(200); + expect(swapResponse.body.result).toHaveLength(1); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_main.spec.ts new file mode 100644 index 0000000000000..ac80d325b590b --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/swap_references_main.spec.ts @@ -0,0 +1,123 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + KBN_ARCHIVE_SAVED_OBJECTS_BASIC, + SERVICE_KEY, + SWAP_REFERENCES_PATH, + SWAP_REFERENCES_PREVIEW_PATH, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${SWAP_REFERENCES_PATH} - main (data view api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let dataViewId: string; + const prevDataViewId = '91200a00-9efd-11e7-acb3-3dab96693fab'; + + apiTest.beforeAll(async ({ apiClient, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + + const createResponse = await apiClient.post(DATA_VIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY]: { title: 'logs-*' } }, + }); + // Fail fast in setup so suite-level errors don't cascade as confusing assertion errors. + expect(createResponse).toHaveStatusCode(200); + dataViewId = createResponse.body[SERVICE_KEY].id; + }); + + apiTest.beforeEach(async ({ kbnClient }) => { + await kbnClient.importExport.load(KBN_ARCHIVE_SAVED_OBJECTS_BASIC); + }); + + apiTest.afterEach(async ({ kbnClient }) => { + await kbnClient.importExport.unload(KBN_ARCHIVE_SAVED_OBJECTS_BASIC); + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (dataViewId) { + await apiServices.dataViews.delete(dataViewId); + } + }); + + apiTest('can preview', async ({ apiClient }) => { + const response = await apiClient.post(SWAP_REFERENCES_PREVIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: prevDataViewId, + toId: dataViewId, + }, + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('can preview specifying type', async ({ apiClient }) => { + const response = await apiClient.post(SWAP_REFERENCES_PREVIEW_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: prevDataViewId, + fromType: 'index-pattern', + toId: dataViewId, + }, + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('can save changes', async ({ apiClient }) => { + const response = await apiClient.post(SWAP_REFERENCES_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: prevDataViewId, + toId: dataViewId, + }, + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toHaveLength(1); + expect(response.body.result[0].id).toBe('dd7caf20-9efd-11e7-acb3-3dab96693fab'); + expect(response.body.result[0].type).toBe('visualization'); + }); + + apiTest('can save changes and remove old saved object', async ({ apiClient }) => { + const response = await apiClient.post(SWAP_REFERENCES_PATH, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fromId: prevDataViewId, + toId: dataViewId, + delete: true, + }, + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toHaveLength(1); + expect(response.body.deleteStatus.remainingRefs).toBe(0); + expect(response.body.deleteStatus.deletePerformed).toBe(true); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH}/${prevDataViewId}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(404); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/params.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/params.spec.ts new file mode 100644 index 0000000000000..326c11f7ee3c1 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/params.spec.ts @@ -0,0 +1,108 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + ES_ARCHIVE_BASIC_INDEX, + EXISTING_INDICES_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +apiTest.describe(`GET /${EXISTING_INDICES_PATH} - params`, { tag: tags.deploymentAgnostic }, () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest('requires a query param', async ({ apiClient }) => { + const response = await apiClient.get(EXISTING_INDICES_PATH, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + }); + + apiTest('accepts indices param as single index string', async ({ apiClient }) => { + const response = await apiClient.get(`${EXISTING_INDICES_PATH}?indices=filebeat-*`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('accepts indices param as single index array', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('indices', 'filebeat-*'); + const response = await apiClient.get(`${EXISTING_INDICES_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('accepts indices param as array', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('indices', 'filebeat-*'); + params.append('indices', 'packetbeat-*'); + const response = await apiClient.get(`${EXISTING_INDICES_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('rejects unexpected query params', async ({ apiClient }) => { + const response = await apiClient.get( + `${EXISTING_INDICES_PATH}?unexpectedParam=unexpectedValue`, + { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(400); + }); + + apiTest('rejects a comma-separated list of indices', async ({ apiClient }) => { + const response = await apiClient.get( + `${EXISTING_INDICES_PATH}?indices=filebeat-*,packetbeat-*`, + { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(400); + }); +}); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/response.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/response.spec.ts new file mode 100644 index 0000000000000..c518bc29e0a52 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/existing_indices_route/response.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + ES_ARCHIVE_BASIC_INDEX, + EXISTING_INDICES_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET /${EXISTING_INDICES_PATH} - response`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest('returns an array of existing indices', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('indices', 'basic_index'); + params.append('indices', 'bad_index'); + + const response = await apiClient.get(`${EXISTING_INDICES_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(['basic_index']); + }); + + apiTest('returns an empty array when no indices exist', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('indices', 'bad_index'); + + const response = await apiClient.get(`${EXISTING_INDICES_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual([]); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/conflicts.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/conflicts.spec.ts new file mode 100644 index 0000000000000..836a61695d429 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/conflicts.spec.ts @@ -0,0 +1,87 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + ES_ARCHIVE_CONFLICTS, + FIELDS_FOR_WILDCARD_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET /${FIELDS_FOR_WILDCARD_PATH} - conflicts`, + { tag: tags.deploymentAgnostic }, + () => { + let viewerApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + // Route only reads field metadata, so `viewer` is sufficient. + viewerApiCredentials = await requestAuth.getApiKey('viewer'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_CONFLICTS); + }); + + apiTest('flags fields with mismatched types as conflicting', async ({ apiClient }) => { + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?pattern=logs-2017.01.*`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual({ + 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'], + }); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/filter.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/filter.spec.ts new file mode 100644 index 0000000000000..23cc972c278df --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/filter.spec.ts @@ -0,0 +1,60 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { FIELDS_FOR_WILDCARD_PATH, INTERNAL_COMMON_HEADERS } from '../../fixtures/constants'; + +apiTest.describe( + `PUT /${FIELDS_FOR_WILDCARD_PATH} - filter fields`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ esClient, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + + await esClient.index({ + index: 'helloworld1', + refresh: true, + id: 'helloworld', + document: { hello: 'world' }, + }); + + await esClient.index({ + index: 'helloworld2', + refresh: true, + id: 'helloworld2', + document: { bye: 'world' }, + }); + }); + + apiTest.afterAll(async ({ esClient }) => { + await esClient.indices.delete({ index: 'helloworld1', ignore_unavailable: true }); + await esClient.indices.delete({ index: 'helloworld2', ignore_unavailable: true }); + }); + + apiTest('can filter', async ({ apiClient }) => { + const response = await apiClient.put(`${FIELDS_FOR_WILDCARD_PATH}?pattern=helloworld*`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { index_filter: { exists: { field: 'bye' } } }, + }); + + expect(response).toHaveStatusCode(200); + + const fieldNames = response.body.fields.map((fld: { name: string }) => fld.name); + expect(fieldNames).toContain('bye'); + expect(fieldNames).not.toContain('hello'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/params.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/params.spec.ts new file mode 100644 index 0000000000000..8135ba1c09e8f --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/params.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + ES_ARCHIVE_BASIC_INDEX, + FIELDS_FOR_WILDCARD_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET /${FIELDS_FOR_WILDCARD_PATH} - params`, + { tag: tags.deploymentAgnostic }, + () => { + let viewerApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + // Route only reads field metadata, so `viewer` is sufficient. + viewerApiCredentials = await requestAuth.getApiKey('viewer'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest('requires a pattern query param', async ({ apiClient }) => { + const response = await apiClient.get(FIELDS_FOR_WILDCARD_PATH, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + }); + + apiTest('accepts include_unmapped param', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('include_unmapped', 'true'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('rejects unexpected query params', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', 'basic_index'); + params.append('unexpected_param', 'unexpected_value'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + }); + + apiTest('fields - accepts a JSON formatted fields query param', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('fields', JSON.stringify(['baz'])); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('fields - accepts fields query param in string array', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('fields', 'baz'); + params.append('fields', 'foo'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('fields - accepts single array fields query param', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('fields', 'baz'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('fields - accepts single fields query param', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('fields', 'baz'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest('fields - rejects a comma-separated list of fields', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('fields', 'foo,bar'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + }); + + apiTest( + 'meta_fields - accepts a JSON formatted meta_fields query param', + async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('meta_fields', JSON.stringify(['meta'])); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + } + ); + + apiTest( + 'meta_fields - accepts meta_fields query param in string array', + async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('meta_fields', '_id'); + params.append('meta_fields', 'meta'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + } + ); + + apiTest('meta_fields - accepts single meta_fields query param', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('meta_fields', '_id'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + }); + + apiTest( + 'meta_fields - rejects a comma-separated list of meta_fields', + async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', '*'); + params.append('meta_fields', 'foo,bar'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...viewerApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(400); + } + ); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/response.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/response.spec.ts new file mode 100644 index 0000000000000..9eb607da39aec --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/fields_for_wildcard_route/response.spec.ts @@ -0,0 +1,359 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { sortBy } from 'lodash'; +import { + ES_ARCHIVE_BASIC_INDEX, + FIELDS_FOR_WILDCARD_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +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, + }, +]; + +apiTest.describe( + `GET /${FIELDS_FOR_WILDCARD_PATH} - response`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + // Tracks the temporary index created by the "returns empty set" test so cleanup runs + // from a hook even if the test times out before reaching its inline cleanup. + let tempIndexName: string | undefined; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterAll(async ({ esClient }) => { + if (tempIndexName) { + await esClient.indices.delete({ index: tempIndexName, ignore_unavailable: true }); + tempIndexName = undefined; + } + }); + + apiTest('returns a flattened version of the fields in es', async ({ apiClient }) => { + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?pattern=basic_index`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual({ + fields: testFields, + indices: ['basic_index'], + }); + + const sortedFields = sortBy(response.body.fields, 'name'); + expect(response.body.fields).toStrictEqual(sortedFields); + }); + + apiTest('returns a single field as requested', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', 'basic_index'); + params.append('fields', JSON.stringify(['bar'])); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual({ + fields: [testFields[0]], + indices: ['basic_index'], + }); + }); + + apiTest('always returns a field for all passed meta fields', async ({ apiClient }) => { + const params = new URLSearchParams(); + params.append('pattern', 'basic_index'); + params.append('meta_fields', JSON.stringify(['_id', '_source', 'crazy_meta_field'])); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + + const expectedFields = [ + { + 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, + }, + ]; + + expect(response.body).toStrictEqual({ + fields: expectedFields, + indices: ['basic_index'], + }); + + const sortedFields = sortBy(response.body.fields, 'name'); + expect(response.body.fields).toStrictEqual(sortedFields); + }); + + apiTest( + 'returns fields when one pattern exists and the other does not', + async ({ apiClient }) => { + const response = await apiClient.get( + `${FIELDS_FOR_WILDCARD_PATH}?pattern=bad_index,basic_index`, + { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual({ + fields: testFields, + indices: ['basic_index'], + }); + } + ); + + apiTest('returns 404 when neither exists', async ({ apiClient }) => { + const response = await apiClient.get( + `${FIELDS_FOR_WILDCARD_PATH}?pattern=bad_index,bad_index_2`, + { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns 404 when no patterns exist', async ({ apiClient }) => { + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?pattern=bad_index`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns nested field metadata', async ({ apiClient }) => { + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?pattern=basic_index`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + + const nestedField = response.body.fields.find( + (f: { name: string }) => f.name === 'nestedField.child' + ); + expect(nestedField).toBeDefined(); + expect(nestedField.subType).toStrictEqual({ + nested: { + path: 'nestedField', + }, + }); + }); + + apiTest('returns multi-field metadata', async ({ apiClient }) => { + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?pattern=basic_index`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + + const multiField = response.body.fields.find( + (f: { name: string }) => f.name === 'baz.keyword' + ); + expect(multiField).toBeDefined(); + expect(multiField.subType).toStrictEqual({ + multi: { + parent: 'baz', + }, + }); + }); + + apiTest( + 'returns empty set when no fields even if meta fields are supplied', + async ({ apiClient, esClient }) => { + // Unique per run so concurrent re-runs don't collide; cleanup happens in afterAll. + const indexName = `fields-for-wildcard-${Date.now()}`; + tempIndexName = indexName; + await esClient.indices.create({ index: indexName }); + + const params = new URLSearchParams(); + params.append('pattern', indexName); + params.append('meta_fields', '_id'); + params.append('meta_fields', '_index'); + + const response = await apiClient.get(`${FIELDS_FOR_WILDCARD_PATH}?${params.toString()}`, { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual({ + fields: [], + indices: [indexName], + }); + } + ); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_errors.spec.ts new file mode 100644 index 0000000000000..b3bbcb521395e --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_errors.spec.ts @@ -0,0 +1,166 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH_LEGACY}/{id}/fields - errors (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns 404 error on non-existing index pattern', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: {}, + }, + }, + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when "fields" payload attribute is invalid', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY_LEGACY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: 123, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe( + '[request body.fields]: expected value of type [object] but got [number]' + ); + }); + + apiTest('returns error if no changes are specified', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY_LEGACY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: {}, + bar: {}, + baz: {}, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe('Change set is empty.'); + }); + + apiTest('returns validation error for too long customDescription', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + [SERVICE_KEY_LEGACY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + fields: { + foo: { + customDescription: 'too long value'.repeat(50), + }, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toContain('it must have a maximum length'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_main.spec.ts new file mode 100644 index 0000000000000..7de09e26cb52e --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/fields_api_update_main.spec.ts @@ -0,0 +1,364 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH_LEGACY}/{id}/fields - main (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + // Shared basic_index-backed index pattern (has real fields like 'foo') used by tests that + // exercise attribute updates on existing fields. + let sharedIndexPatternId: string; + + apiTest.beforeAll(async ({ esArchiver, requestAuth, apiClient }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY_LEGACY]: { title: 'ba*ic_index' } }, + }); + // Fail fast in setup so suite-level errors don't cascade as confusing assertion errors. + expect(createResponse).toHaveStatusCode(200); + sharedIndexPatternId = createResponse.body[SERVICE_KEY_LEGACY].id; + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (sharedIndexPatternId) { + await apiServices.dataViews.delete(sharedIndexPatternId); + } + }); + + apiTest('can update multiple fields', async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${dataView.id}/fields`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + foo: { count: 123, customLabel: 'test' }, + bar: { count: 456, customDescription: 'desc' }, + }, + }, + } + ); + + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(123); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBe('test'); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.bar.count).toBe(456); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.bar.customDescription).toBe('desc'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(123); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.bar.count).toBe(456); + }); + + apiTest( + 'can set field count attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${dataView.id}/fields`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: 123 } } }, + } + ); + + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(123); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(123); + } + ); + + apiTest('can update count attribute in attribute map', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY_LEGACY]: { title, fieldAttrs: { foo: { count: 1 } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + expect(createResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(1); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: 2 } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(2); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBe(2); + }); + + apiTest('can delete count attribute from attribute map', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY_LEGACY]: { title, fieldAttrs: { foo: { count: 1 } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { count: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.count).toBeUndefined(); + }); + + apiTest( + 'can set field customLabel attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${dataView.id}/fields`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'foo' } } }, + } + ); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBe('foo'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBe('foo'); + } + ); + + apiTest('can update customLabel attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY_LEGACY]: { title, fieldAttrs: { foo: { customLabel: 'foo' } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'bar' } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBe('bar'); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBe('bar'); + }); + + apiTest('can set field customLabel attribute on an existing field', async ({ apiClient }) => { + await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${sharedIndexPatternId}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: 'baz' } } }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${sharedIndexPatternId}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY_LEGACY].fields.foo.customLabel).toBe('baz'); + }); + + apiTest('can delete customLabel attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { [SERVICE_KEY_LEGACY]: { title, fieldAttrs: { foo: { customLabel: 'foo' } } } }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { customLabel: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldAttrs.foo.customLabel).toBeUndefined(); + }); + + apiTest( + 'can set field format attribute on non-existing field', + async ({ apiClient, apiServices }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const { data: dataView } = await apiServices.dataViews.create({ title, override: true }); + createdIds.push(dataView.id); + + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${dataView.id}/fields`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: { id: 'bar', params: { baz: 'qux' } } } } }, + } + ); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toStrictEqual({ + id: 'bar', + params: { baz: 'qux' }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${dataView.id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toStrictEqual({ + id: 'bar', + params: { baz: 'qux' }, + }); + } + ); + + apiTest('can update format attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + [SERVICE_KEY_LEGACY]: { + title, + fieldFormats: { foo: { id: 'bar', params: { baz: 'qux' } } }, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: { id: 'bar-2', params: { baz: 'qux-2' } } } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toStrictEqual({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toStrictEqual({ + id: 'bar-2', + params: { baz: 'qux-2' }, + }); + }); + + apiTest('can remove format attribute', async ({ apiClient }) => { + const title = `foo-${Date.now()}-${Math.random()}*`; + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + [SERVICE_KEY_LEGACY]: { + title, + fieldFormats: { foo: { id: 'bar', params: { baz: 'qux' } } }, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const updateResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { fields: { foo: { format: null } } }, + }); + expect(updateResponse).toHaveStatusCode(200); + expect(updateResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toBeUndefined(); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + expect(getResponse.body[SERVICE_KEY_LEGACY].fieldFormats.foo).toBeUndefined(); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/has_user_index_pattern.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/has_user_index_pattern.spec.ts new file mode 100644 index 0000000000000..12569acc1169c --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/has_user_index_pattern.spec.ts @@ -0,0 +1,115 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + HAS_USER_INDEX_PATTERN_PATH, + INTERNAL_COMMON_HEADERS, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET ${HAS_USER_INDEX_PATTERN_PATH} (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + // The "returns false" assertion needs a guaranteed empty starting state. Instead of + // `cleanStandardList()` (a cluster-wide saved-object wipe that can break other suites + // sharing the deployment) we narrowly delete just the existing index patterns and the + // two test indices the suite reads from. + apiTest.beforeEach(async ({ esClient, apiServices }) => { + const { data: existing } = await apiServices.dataViews.getAll(); + for (const dv of existing) { + await apiServices.dataViews.delete(dv.id); + } + for (const index of ['metrics-test', 'logs-test']) { + if (await esClient.indices.exists({ index })) { + await esClient.indices.delete({ index }); + } + } + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns false if no index patterns exist', async ({ apiClient }) => { + const response = await apiClient.get(HAS_USER_INDEX_PATTERN_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(false); + }); + + apiTest( + 'returns true if has index pattern with user data', + async ({ apiClient, esArchiver }) => { + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { title: 'basic_index' }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + createdIds.push(createResponse.body[SERVICE_KEY_LEGACY].id); + + const response = await apiClient.get(HAS_USER_INDEX_PATTERN_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(true); + } + ); + + apiTest('returns true if has user index pattern without data', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title: 'basic_index', + allowNoIndex: true, + }, + }, + }); + expect(createResponse).toHaveStatusCode(200); + createdIds.push(createResponse.body[SERVICE_KEY_LEGACY].id); + + const response = await apiClient.get(HAS_USER_INDEX_PATTERN_PATH, { + headers: { ...INTERNAL_COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.result).toBe(true); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/integration.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/integration.spec.ts new file mode 100644 index 0000000000000..8fe24b868df57 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/integration.spec.ts @@ -0,0 +1,149 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { omit } from 'lodash'; +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + 'index patterns integration (legacy index pattern api)', + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest( + 'create an index pattern, add a runtime field, add a field formatter, then re-create the same index pattern', + async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + // Track immediately so cleanup runs even if the test fails before the recreate step. + createdIds.push(id); + + const runtimeFieldResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + } + ); + + expect(runtimeFieldResponse).toHaveStatusCode(200); + + const fieldAttrsResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + runtimeBar: { count: 123, customLabel: 'test' }, + }, + }, + }); + + expect(fieldAttrsResponse).toHaveStatusCode(200); + + const formatterResponse = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/fields`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + fields: { + runtimeBar: { + format: { + id: 'duration', + params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' }, + }, + }, + }, + }, + }); + + expect(formatterResponse).toHaveStatusCode(200); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + + const resultIndexPattern = getResponse.body[SERVICE_KEY_LEGACY]; + + const runtimeField = resultIndexPattern.fields.runtimeBar; + expect(runtimeField.name).toBe('runtimeBar'); + expect(runtimeField.runtimeField.type).toBe('long'); + expect(runtimeField.runtimeField.script.source).toBe("emit(doc['field_name'].value)"); + expect(runtimeField.scripted).toBe(false); + + expect(resultIndexPattern.fieldFormats.runtimeBar.id).toBe('duration'); + expect(resultIndexPattern.fieldFormats.runtimeBar.params.inputFormat).toBe('milliseconds'); + expect(resultIndexPattern.fieldFormats.runtimeBar.params.outputFormat).toBe( + 'humanizePrecise' + ); + + expect(resultIndexPattern.fieldAttrs.runtimeBar.count).toBe(123); + expect(resultIndexPattern.fieldAttrs.runtimeBar.customLabel).toBe('test'); + + const recreateResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: resultIndexPattern, + }, + }); + + expect(recreateResponse).toHaveStatusCode(200); + const recreatedIndexPattern = recreateResponse.body[SERVICE_KEY_LEGACY]; + // `override: true` reuses the same id, so the original push above already covers cleanup. + + // The retrieved object should be transient, so a clone re-created from it should + // match the original (ignoring `version`/`namespaces` which are assigned server-side). + expect(omit(recreatedIndexPattern, ['version', 'namespaces'])).toStrictEqual( + omit(resultIndexPattern, ['version', 'namespaces']) + ); + } + ); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_errors.spec.ts new file mode 100644 index 0000000000000..12ac3fbb0b102 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_errors.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, +} from '../../fixtures/constants'; + +apiTest.describe( + `DELETE ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field/{name} - errors (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let indexPatternId: string; + + apiTest.beforeAll(async ({ esArchiver, requestAuth, apiServices }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + + const { data: dataView } = await apiServices.dataViews.create({ + title: 'b*sic_index', + override: true, + }); + indexPatternId = dataView.id; + }); + + apiTest.afterAll(async ({ apiServices }) => { + if (indexPatternId) { + await apiServices.dataViews.delete(indexPatternId); + } + }); + + apiTest('returns 404 error on non-existing index pattern', async ({ apiClient }) => { + const nonExistentId = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + + const response = await apiClient.delete( + `${DATA_VIEW_PATH_LEGACY}/${nonExistentId}/runtime_field/foo`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns 404 error on non-existing runtime field', async ({ apiClient }) => { + const response = await apiClient.delete( + `${DATA_VIEW_PATH_LEGACY}/${indexPatternId}/runtime_field/test`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when ID is too long', async ({ apiClient }) => { + const longId = 'x'.repeat(1100); + + const response = await apiClient.delete( + `${DATA_VIEW_PATH_LEGACY}/${longId}/runtime_field/foo`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(400); + expect(response.body.message).toContain('must have a maximum length of [1000]'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_main.spec.ts new file mode 100644 index 0000000000000..7084fe71fde22 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_delete_main.spec.ts @@ -0,0 +1,101 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `DELETE ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field/{name} - main (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can delete a runtime field', async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title, + runtimeFieldMap: { + runtimeBar: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const getResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(getResponse).toHaveStatusCode(200); + expect(typeof getResponse.body[SERVICE_KEY_LEGACY].fields.runtimeBar).toBe('object'); + + const deleteResponse = await apiClient.delete( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeBar`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(deleteResponse).toHaveStatusCode(200); + + const verifyResponse = await apiClient.get(`${DATA_VIEW_PATH_LEGACY}/${id}`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + }); + + expect(verifyResponse).toHaveStatusCode(200); + expect(verifyResponse.body[SERVICE_KEY_LEGACY].fields.runtimeBar).toBeUndefined(); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_get_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_get_main.spec.ts new file mode 100644 index 0000000000000..f0e332d70afd4 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_get_main.spec.ts @@ -0,0 +1,93 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `GET ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field/{name} - main (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can fetch a runtime field', async ({ apiClient }) => { + const title = `basic_index*`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title, + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "emit(doc['field_name'].value)" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const getResponse = await apiClient.get( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeFoo`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + } + ); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY_LEGACY]).toBeDefined(); + + // The legacy endpoint returns the runtime field under a singular `field` key; the modern + // data-view endpoint uses `fields[0]` instead. + const field = getResponse.body.field; + expect(typeof field).toBe('object'); + expect(field.name).toBe('runtimeFoo'); + expect(field.type).toBe('string'); + expect(field.scripted).toBe(false); + expect(field.runtimeField.script.source).toBe("emit(doc['field_name'].value)"); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_errors.spec.ts new file mode 100644 index 0000000000000..cf9176644b46f --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_errors.spec.ts @@ -0,0 +1,101 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `PUT ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field - errors (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('returns 404 error on non-existing index pattern', async ({ apiClient }) => { + const nonExistentId = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + + const response = await apiClient.put( + `${DATA_VIEW_PATH_LEGACY}/${nonExistentId}/runtime_field`, + { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + } + ); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error on non-runtime field update attempt', async ({ apiClient }) => { + const title = `basic_index`; + + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { title }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + // 'bar' is a regular field from basic_index, not a runtime field. + const putResponse = await apiClient.put(`${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field`, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + responseType: 'json', + body: { + name: 'bar', + runtimeField: { + type: 'long', + script: { source: "emit(doc['field_name'].value)" }, + }, + }, + }); + + expect(putResponse).toHaveStatusCode(400); + expect(putResponse.body.message).toBe('Only runtime fields can be updated'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_main.spec.ts new file mode 100644 index 0000000000000..ef5a228cec337 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_put_main.spec.ts @@ -0,0 +1,151 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `PUT ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field - main (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest('can overwrite an existing field', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + const putResponse = await apiClient.put(`${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeFoo', + runtimeField: { + type: 'long', + script: { source: "doc['field_name'].value" }, + }, + }, + }); + + expect(putResponse).toHaveStatusCode(200); + expect(putResponse.body[SERVICE_KEY_LEGACY]).toBeDefined(); + + const fooResponse = await apiClient.get( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(fooResponse).toHaveStatusCode(200); + // Legacy endpoint returns `body.field` (singular) rather than `body.fields[0]`. + // After overwriting, runtimeFoo should now be a number (long) rather than a string (keyword). + expect(fooResponse.body.field.type).toBe('number'); + + const barResponse = await apiClient.get( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeBar`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(barResponse).toHaveStatusCode(200); + // runtimeBar should be unaffected - still keyword (type string) + expect(barResponse.body.field.type).toBe('string'); + }); + + apiTest('can add a new runtime field', async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + await apiClient.put(`${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'runtimeBar', + runtimeField: { + type: 'long', + script: { source: "doc['field_name'].value" }, + }, + }, + }); + + const getResponse = await apiClient.get( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeBar`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + } + ); + + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY_LEGACY]).toBeDefined(); + // Legacy endpoint returns `body.field` (singular) rather than `body.fields[0]`. + expect(typeof getResponse.body.field.runtimeField).toBe('object'); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_errors.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_errors.spec.ts new file mode 100644 index 0000000000000..ec48478754bce --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_errors.spec.ts @@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { COMMON_HEADERS, DATA_VIEW_PATH_LEGACY } from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field/{name} - update errors (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest('returns 404 error on non-existing index pattern', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/foo`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest('returns error when field name is specified in body', async ({ apiClient }) => { + const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; + // The update endpoint should not accept a `name` in the body - it comes from the URL. + const response = await apiClient.post(`${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/foo`, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + name: 'foo', + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + }); + + expect(response).toHaveStatusCode(400); + expect(response.body.statusCode).toBe(400); + expect(response.body.message).toBe( + "[request body.name]: a value wasn't expected to be present" + ); + }); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_main.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_main.spec.ts new file mode 100644 index 0000000000000..0f27e6c750cd6 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/index_patterns/runtime_fields_update_main.spec.ts @@ -0,0 +1,122 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH_LEGACY, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY_LEGACY, +} from '../../fixtures/constants'; + +apiTest.describe( + `POST ${DATA_VIEW_PATH_LEGACY}/{id}/runtime_field/{name} - update main (legacy index pattern api)`, + { tag: tags.deploymentAgnostic }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdIds: string[] = []; + + apiTest.beforeAll(async ({ esArchiver, requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + for (const id of createdIds) { + await apiServices.dataViews.delete(id); + } + createdIds = []; + }); + + apiTest( + 'can update an existing runtime field and do a partial update', + async ({ apiClient }) => { + const createResponse = await apiClient.post(DATA_VIEW_PATH_LEGACY, { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + override: true, + [SERVICE_KEY_LEGACY]: { + title: 'basic_index', + runtimeFieldMap: { + runtimeFoo: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + runtimeBar: { + type: 'keyword', + script: { source: "doc['field_name'].value" }, + }, + }, + }, + }, + }); + + expect(createResponse).toHaveStatusCode(200); + const id = createResponse.body[SERVICE_KEY_LEGACY].id; + createdIds.push(id); + + await apiTest.step('full update of runtimeFoo', async () => { + const updateResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + type: 'keyword', + script: { source: "doc['something_new'].value" }, + }, + }, + } + ); + expect(updateResponse).toHaveStatusCode(200); + + const getResponse = await apiClient.get( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(getResponse).toHaveStatusCode(200); + expect(getResponse.body[SERVICE_KEY_LEGACY]).toBeDefined(); + + // Legacy endpoint returns `body.field` (singular) rather than `body.fields[0]`. + const field = getResponse.body.field; + expect(field.type).toBe('string'); + expect(field.runtimeField.type).toBe('keyword'); + expect(field.runtimeField.script.source).toBe("doc['something_new'].value"); + }); + + await apiTest.step('partial update of runtimeFoo (only script)', async () => { + const partialResponse = await apiClient.post( + `${DATA_VIEW_PATH_LEGACY}/${id}/runtime_field/runtimeFoo`, + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + responseType: 'json', + body: { + runtimeField: { + script: { source: "doc['partial_update'].value" }, + }, + }, + } + ); + + expect(partialResponse).toHaveStatusCode(200); + // Legacy endpoint returns `body.field` (singular) rather than `body.fields[0]`. + expect(partialResponse.body.field.runtimeField.script.source).toBe( + "doc['partial_update'].value" + ); + }); + } + ); + } +); diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/constants.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/constants.ts deleted file mode 100644 index 4292c1da7fa78..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/constants.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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]; diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/index.ts deleted file mode 100644 index 4b04c0d03df28..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/main.ts deleted file mode 100644 index 1766522014b22..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/main.ts +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 with count and label when creating an index pattern', async () => { - const title = `foo-${Date.now()}-${Math.random()}*`; - const response = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - const response2 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - timeFieldName: 'foo', - }, - }); - const response2 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/validation.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/validation.ts deleted file mode 100644 index 4364165d518b0..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/create_data_view/validation.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('validation', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - configArray.forEach((config) => { - describe(config.name, () => { - it('returns error when index_pattern object is not provided', async () => { - const response = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - runtimeFieldMap: { - runtimeFoo: { - type: 'wrong-type', - script: { - source: 'emit(doc["foo"].value)', - }, - }, - }, - }, - }); - - expect(response.status).to.be(400); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/errors.ts deleted file mode 100644 index 62a1c24bd1a86..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/errors.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .delete(`${config.path}/${id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .delete(`${config.path}/${id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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].' - ); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/index.ts deleted file mode 100644 index 234932b17e81d..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/delete_data_view/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/index.ts deleted file mode 100644 index 8538e6f1dc529..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/data_views_crud/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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('./delete_data_view')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/index.ts deleted file mode 100644 index b01f70f4e6565..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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/_existing_indices route', () => { - loadTestFile(require.resolve('./params')); - loadTestFile(require.resolve('./response')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/params.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/params.ts deleted file mode 100644 index 45530ea976f07..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/params.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 { EXISTING_INDICES_PATH } from '@kbn/data-views-plugin/common/constants'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const randomness = getService('randomness'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('_existing_indices params', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('requires a query param', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({}) - .expect(400)); - - it('accepts indices param as single index string', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: 'filebeat-*', - }) - .expect(200)); - - it('accepts indices param as single index array', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: ['filebeat-*'], - }) - .expect(200)); - - it('accepts indices param', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: ['filebeat-*', 'packetbeat-*'], - }) - .expect(200)); - - it('rejects unexpected query params', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - [randomness.word()]: randomness.word(), - }) - .expect(400)); - - it('rejects a comma-separated list of indices', () => - supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: 'filebeat-*,packetbeat-*', - }) - .expect(400)); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/response.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/response.ts deleted file mode 100644 index c79b90dd1b015..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/existing_indices_route/response.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 { EXISTING_INDICES_PATH } from '@kbn/data-views-plugin/common/constants'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('_existing_indices response', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('returns an array of existing indices', async () => { - await supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: ['basic_index', 'bad_index'], - }) - .expect(200, ['basic_index']); - }); - - it('returns an empty array when no indices exist', async () => { - await supertestWithoutAuth - .get(EXISTING_INDICES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - indices: ['bad_index'], - }) - .expect(200, []); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/index.ts deleted file mode 100644 index 53b2e2e069edd..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/errors.ts deleted file mode 100644 index 5b8e18279ed35..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/errors.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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 { RoleCredentials, InternalRequestHeader } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .post(`${config.path}/${id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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.'); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/index.ts deleted file mode 100644 index 76da56664b95c..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/main.ts deleted file mode 100644 index 434ba12f07d54..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_api/update_fields/main.ts +++ /dev/null @@ -1,534 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - describe('main', () => { - const basicIndex = 'ba*ic_index'; - let indexPattern: any; - - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - before(async () => { - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - indexPattern = ( - await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title: basicIndex, - }, - }) - ).body[config.serviceKey]; - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - if (indexPattern) { - await supertestWithoutAuth - .delete(`${config.path}/${indexPattern.id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - } - }); - - it('can update multiple fields', async () => { - const title = `foo-${Date.now()}-${Math.random()}*`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - - expect(response1.status).to.be(200); - expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined); - - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - - expect(response1.status).to.be(200); - expect(response1.body[config.serviceKey].fieldAttrs.foo).to.be(undefined); - - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(`${config.path}/${indexPattern.id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fields: { - foo: { - customLabel: 'baz', - }, - }, - }); - - const response1 = await supertestWithoutAuth - .get(`${config.path}/${indexPattern.id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - - expect(response1.status).to.be(200); - expect(response1.body[config.serviceKey].fieldFormats.foo).to.be(undefined); - - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(`${config.path}/${indexPattern.id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fields: { - foo: { - format: null, - }, - }, - }); - - expect(response2.status).to.be(200); - expect(response2.body[config.serviceKey].fieldFormats.foo).to.be(undefined); - - const response3 = await supertestWithoutAuth - .get(`${config.path}/${indexPattern.id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/conflicts.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/conflicts.ts deleted file mode 100644 index cec2d75ad556e..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/conflicts.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('conflicts', function () { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/conflicts' - ); - }); - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/conflicts' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('flags fields with mismatched types as conflicting', async () => { - const resp = await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ pattern: 'logs-2017.01.*' }) - .expect(200); - 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'], - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/filter.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/filter.ts deleted file mode 100644 index 3f26e407e5d1e..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/filter.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const es = getService('es'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('filter fields', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await es.index({ - index: 'helloworld1', - refresh: true, - id: 'helloworld', - body: { hello: 'world' }, - }); - - await es.index({ - index: 'helloworld2', - refresh: true, - id: 'helloworld2', - body: { bye: 'world' }, - }); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('can filter', async () => { - const a = await supertestWithoutAuth - .put(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/index.ts deleted file mode 100644 index f4567a55e8efb..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/params.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/params.ts deleted file mode 100644 index d29d2ae12a236..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/params.ts +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const randomness = getService('randomness'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('params', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('requires a pattern query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({}) - .expect(400); - }); - - it('accepts include_unmapped param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - include_unmapped: true, - }) - .expect(200); - }); - - it('rejects unexpected query params', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: randomness.word(), - [randomness.word()]: randomness.word(), - }) - .expect(400); - }); - - describe('fields', () => { - it('accepts a JSON formatted fields query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - fields: JSON.stringify(['baz']), - }) - .expect(200); - }); - - it('accepts meta_fields query param in string array', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - fields: ['baz', 'foo'], - }) - .expect(200); - }); - - it('accepts single array fields query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - fields: ['baz'], - }) - .expect(200); - }); - - it('accepts single fields query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - fields: 'baz', - }) - .expect(200); - }); - - it('rejects a comma-separated list of fields', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - fields: 'foo,bar', - }) - .expect(400); - }); - }); - - describe('meta_fields', () => { - it('accepts a JSON formatted meta_fields query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - meta_fields: JSON.stringify(['meta']), - }) - .expect(200); - }); - - it('accepts meta_fields query param in string array', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - meta_fields: ['_id', 'meta'], - }) - .expect(200); - }); - - it('accepts single meta_fields query param', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - meta_fields: ['_id'], - }) - .expect(200); - }); - - it('rejects a comma-separated list of meta_fields', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: '*', - meta_fields: 'foo,bar', - }) - .expect(400); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/response.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/response.ts deleted file mode 100644 index 82ea83543ee17..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/fields_for_wildcard_route/response.ts +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - 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(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - it('returns a flattened version of the fields in es', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ pattern: 'basic_index' }) - .expect(200, { - fields: testFields, - indices: ['basic_index'], - }) - .then(ensureFieldsAreSorted); - }); - - it('returns a single field as requested', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ pattern: 'bad_index,basic_index' }) - .expect(200, { - fields: testFields, - indices: ['basic_index'], - }); - }); - - it('returns 404 when neither exists', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ pattern: 'bad_index,bad_index_2' }) - .expect(404); - }); - - it('returns 404 when no patterns exist', async () => { - await supertestWithoutAuth - .get(FIELDS_FOR_WILDCARD_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .query({ - pattern: 'bad_index', - }) - .expect(404); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/has_user_index_pattern.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/has_user_index_pattern.ts deleted file mode 100644 index 35dc8264e50f7..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/has_user_index_pattern.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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'; -import type { RoleCredentials, InternalRequestHeader } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const es = getService('es'); - const kibanaServer = getService('kibanaServer'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('has user index pattern API', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .get(servicePath) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - 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( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await supertestWithoutAuth - .post(config.path) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title: 'basic_index', - }, - }); - - const response = await supertestWithoutAuth - .get(servicePath) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - expect(response.status).to.be(200); - expect(response.body.result).to.be(true); - - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - it('should return true if has user index pattern without data', async () => { - await supertestWithoutAuth - .post(config.path) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title: 'basic_index', - allowNoIndex: true, - }, - }); - - const response = await supertestWithoutAuth - .get(servicePath) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - expect(response.status).to.be(200); - expect(response.body.result).to.be(true); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/index.ts deleted file mode 100644 index 02c6c8064b61f..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/has_user_index_pattern/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/index.ts index 34fab1ec20767..2e63370e9887d 100644 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/index.ts +++ b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/index.ts @@ -11,18 +11,8 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('index_patterns', function () { this.tags(['esGate']); + // Remaining FTR coverage. The rest of this suite has been migrated to Scout under + // `src/platform/plugins/shared/data_views/test/scout/api/tests/`. loadTestFile(require.resolve('./es_errors')); - loadTestFile(require.resolve('./existing_indices_route')); - 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('./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')); }); } diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/index.ts deleted file mode 100644 index 0279fc8744fd1..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/integration.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/integration.ts deleted file mode 100644 index c77b58b8e5cf8..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/integration/integration.ts +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; - -/** - * Test usage of different index patterns APIs in combination - */ -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('integration', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - 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 supertestWithoutAuth - .post('/api/index_patterns/index_pattern') - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - index_pattern: { - title, - }, - }); - const id = response1.body.index_pattern.id; - const response2 = await supertestWithoutAuth - .post(`/api/index_patterns/index_pattern/${id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - expect(response2.status).to.be(200); - - const response3 = await supertestWithoutAuth - .post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fields: { - runtimeBar: { - count: 123, - customLabel: 'test', - }, - }, - }); - - expect(response3.status).to.be(200); - - const response4 = await supertestWithoutAuth - .post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fields: { - runtimeBar: { - format: { - id: 'duration', - params: { inputFormat: 'milliseconds', outputFormat: 'humanizePrecise' }, - }, - }, - }, - }); - - expect(response4.status).to.be(200); - - const response5 = await supertestWithoutAuth - .get('/api/index_patterns/index_pattern/' + response1.body.index_pattern.id) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post('/api/index_patterns/index_pattern') - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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') - ); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/errors.ts deleted file mode 100644 index 6b2e6410f20e1..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/errors.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title, - }, - }); - const id = response1.body[config.serviceKey].id; - const response2 = await supertestWithoutAuth - .post(`${config.path}/${id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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]' - ); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/index.ts deleted file mode 100644 index 7b15085d5e4db..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/main.ts deleted file mode 100644 index 92198b07c4021..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/create_runtime_field/main.ts +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - it('can create a new runtime field', async () => { - const title = `basic_index*`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - }, - }); - const id = response1.body[config.serviceKey].id; - const response2 = await supertestWithoutAuth - .post(`${config.path}/${id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - }, - }); - - await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - const response2 = await supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - expect(response3.status).to.be(400); - - await supertestWithoutAuth - .delete(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - }); - - it('prevents field name collisions', async () => { - const title = `basic*`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - }, - }); - - const response2 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - expect(response2.status).to.be(200); - - const response3 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }); - - expect(response3.status).to.be(400); - - const response4 = await supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/errors.ts deleted file mode 100644 index c302c83053083..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/errors.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - const basicIndex = 'b*sic_index'; - let indexPattern: any; - - configArray.forEach((config) => { - describe(config.name, () => { - before(async () => { - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - indexPattern = ( - await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title: basicIndex, - }, - }) - ).body[config.serviceKey]; - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - if (indexPattern) { - await supertestWithoutAuth - .delete(`${config.path}/${indexPattern.id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - } - }); - - it('returns 404 error on non-existing index_pattern', async () => { - const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; - const response = await supertestWithoutAuth - .delete(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(response.status).to.be(404); - }); - - it('returns 404 error on non-existing runtime field', async () => { - const response1 = await supertestWithoutAuth - .delete(`${config.path}/${indexPattern.id}/runtime_field/test`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .delete(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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].' - ); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/index.ts deleted file mode 100644 index 3a82d46237748..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/main.ts deleted file mode 100644 index e0916a68a9347..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/delete_runtime_field/main.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - it('can delete a runtime field', async () => { - const title = `basic_index*`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - runtimeFieldMap: { - runtimeBar: { - type: 'long', - script: { - source: "emit(doc['field_name'].value)", - }, - }, - }, - }, - }); - - const response2 = await supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(typeof response2.body[config.serviceKey].fields.runtimeBar).to.be('object'); - - const response3 = await supertestWithoutAuth - .delete( - `${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar` - ) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(response3.status).to.be(200); - - const response4 = await supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(typeof response4.body[config.serviceKey].fields.runtimeBar).to.be('undefined'); - await supertestWithoutAuth - .delete(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/errors.ts deleted file mode 100644 index 96fb034c2eed5..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/errors.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - const basicIndex = '*asic_index'; - let indexPattern: any; - - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - before(async () => { - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - indexPattern = ( - await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - [config.serviceKey]: { - title: basicIndex, - }, - }) - ).body[config.serviceKey]; - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - - if (indexPattern) { - await supertestWithoutAuth - .delete(`${config.path}/${indexPattern.id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - } - }); - - it('returns 404 error on non-existing index_pattern', async () => { - const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; - const response = await supertestWithoutAuth - .get(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(response.status).to.be(404); - }); - - it('returns 404 error on non-existing runtime field', async () => { - const response1 = await supertestWithoutAuth - .get(`${config.path}/${indexPattern.id}/runtime_field/sf`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .get(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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].' - ); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/index.ts deleted file mode 100644 index b222b1ab84ebb..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/main.ts deleted file mode 100644 index 6cc0e5798621e..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/get_runtime_field/main.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - it('can fetch a runtime field', async () => { - const title = `basic_index*`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .delete(`${config.path}/${response1.body[config.serviceKey].id}`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/index.ts deleted file mode 100644 index dffa721afea4d..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/errors.ts deleted file mode 100644 index cecb9f0cb4068..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/errors.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 { RoleCredentials, InternalRequestHeader } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - 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 supertestWithoutAuth - .put(`${config.path}/${id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - }, - }); - - const response2 = await supertestWithoutAuth - .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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'); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/index.ts deleted file mode 100644 index 989066177c605..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/main.ts deleted file mode 100644 index 849ff36398373..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/put_runtime_field/main.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - it('can overwrite an existing field', async () => { - const title = `basic_index`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(config.path) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - override: true, - [config.serviceKey]: { - title, - runtimeFieldMap: { - runtimeFoo: { - type: 'keyword', - script: { - source: "doc['field_name'].value", - }, - }, - }, - }, - }); - - await supertestWithoutAuth - .put(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - name: 'runtimeBar', - runtimeField: { - type: 'long', - script: { - source: "doc['field_name'].value", - }, - }, - }); - - const response2 = await supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeBar`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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'); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/errors.ts deleted file mode 100644 index f36f7648cd4a0..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/errors.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - 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 supertestWithoutAuth - .post(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${id}/runtime_field/foo`) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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" - ); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/index.ts deleted file mode 100644 index dbacc90699ce3..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/main.ts deleted file mode 100644 index 7d6d57ce23c1f..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/runtime_fields_crud/update_runtime_field/main.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../../shared/services'; -import type { FtrProviderContext } from '../../../../ftr_provider_context'; -import { configArray } from '../../constants'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('main', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - - configArray.forEach((config) => { - describe(config.name, () => { - it('can update an existing field', async () => { - const title = `basic_index`; - const response1 = await supertestWithoutAuth - .post(config.path) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - runtimeField: { - type: 'keyword', - script: { - source: "doc['something_new'].value", - }, - }, - }); - - expect(response2.status).to.be(200); - - const response3 = await supertestWithoutAuth - .get(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .post(`${config.path}/${response1.body[config.serviceKey].id}/runtime_field/runtimeFoo`) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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"); - }); - }); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/errors.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/errors.ts deleted file mode 100644 index 9576bb85fe825..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/errors.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; -import type { FtrProviderContext } from '../../../ftr_provider_context'; -import { dataViewConfig } from '../constants'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('errors', () => { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - }); - after(async () => { - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - it('returns 404 error on non-existing index_pattern', async () => { - const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`; - const response = await supertestWithoutAuth - .get(`${dataViewConfig.path}/${id}`) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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 supertestWithoutAuth - .get(`${dataViewConfig.path}/${id}`) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - 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].' - ); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/index.ts deleted file mode 100644 index f527c63e478e3..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/main.ts b/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/main.ts deleted file mode 100644 index 2d12669f72a7e..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/data_views/swap_references/main.ts +++ /dev/null @@ -1,219 +0,0 @@ -/* - * 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'; -import type { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - 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 () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - const result = await supertestWithoutAuth - .post(DATA_VIEW_PATH) - .send({ data_view: { title } }) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - dataViewId = result.body.data_view.id; - }); - after(async () => { - await supertestWithoutAuth - .delete(SPECIFIC_DATA_VIEW_PATH.replace('{id}', dataViewId)) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - beforeEach(async () => { - await kibanaServer.importExport.load( - 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' - ); - }); - afterEach(async () => { - await kibanaServer.importExport.unload( - 'src/platform/test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json' - ); - }); - - it('can preview', async () => { - const res = await supertestWithoutAuth - .post(PREVIEW_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fromId: prevDataViewId, - toId: dataViewId, - }); - expect(res).to.have.property('status', 200); - }); - - it('can preview specifying type', async () => { - const res = await supertestWithoutAuth - .post(PREVIEW_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - fromId: prevDataViewId, - fromType: 'index-pattern', - toId: dataViewId, - }); - expect(res).to.have.property('status', 200); - }); - - it('can save changes', async () => { - const res = await supertestWithoutAuth - .post(DATA_VIEW_SWAP_REFERENCES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(DATA_VIEW_SWAP_REFERENCES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .get(SPECIFIC_DATA_VIEW_PATH.replace('{id}', prevDataViewId)) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(res2).to.have.property('statusCode', 404); - }); - - describe('limit affected saved objects', () => { - beforeEach(async () => { - await kibanaServer.importExport.load( - 'src/platform/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json' - ); - }); - afterEach(async () => { - await kibanaServer.importExport.unload( - 'src/platform/test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json' - ); - }); - - it("won't delete if reference remains", async () => { - const res = await supertestWithoutAuth - .post(DATA_VIEW_SWAP_REFERENCES_PATH) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .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 supertestWithoutAuth - .post(PREVIEW_PATH) - .send({ - fromId: '8963ca30-3224-11e8-a572-ffca06da1357', - toId: '91200a00-9efd-11e7-acb3-3dab96693fab', - }) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - expect(res).to.have.property('status', 200); - expect(res.body.result.length).to.equal(2); - - // limit to one item - const res2 = await supertestWithoutAuth - .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) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - 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 supertestWithoutAuth - .post(PREVIEW_PATH) - .send({ - fromId: '8963ca30-3224-11e8-a572-ffca06da1357', - toId: '91200a00-9efd-11e7-acb3-3dab96693fab', - }) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - expect(res).to.have.property('status', 200); - expect(res.body.result.length).to.equal(2); - - // limit to one item - const res2 = await supertestWithoutAuth - .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) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - expect(res2).to.have.property('status', 200); - expect(res2.body.result.length).to.equal(1); - }); - }); - }); -}