diff --git a/.buildkite/ftr_platform_stateful_configs.yml b/.buildkite/ftr_platform_stateful_configs.yml index a25ff862d6ba2..133ba6d261ce9 100644 --- a/.buildkite/ftr_platform_stateful_configs.yml +++ b/.buildkite/ftr_platform_stateful_configs.yml @@ -393,7 +393,6 @@ enabled: - x-pack/platform/test/api_integration/apis/grok_debugger/config.ts - x-pack/platform/test/api_integration/apis/file_upload/config.ts - x-pack/platform/test/api_integration/apis/kibana/config.ts - - x-pack/platform/test/api_integration/apis/logstash/config.ts - x-pack/platform/test/api_integration/apis/management/config.ts - x-pack/platform/test/api_integration/apis/management/index_management/disabled_data_enrichers/config.ts - x-pack/platform/test/api_integration/apis/maps/config.ts diff --git a/.buildkite/scout_ci_config.yml b/.buildkite/scout_ci_config.yml index e78bc8bd5f515..e992e07cbc275 100644 --- a/.buildkite/scout_ci_config.yml +++ b/.buildkite/scout_ci_config.yml @@ -9,6 +9,7 @@ plugins: - exploratory_view - index_management - infra + - logstash - maps - observability - observability_onboarding diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 539eed7c1a99d..25c2e12fba945 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2941,7 +2941,6 @@ x-pack/solutions/security/plugins/security_solution/server/lib/security_integrat /x-pack/platform/test/functional/services/pipeline_* @elastic/logstash /x-pack/platform/test/functional/page_objects/logstash_page.ts @elastic/logstash /x-pack/platform/test/functional/apps/logstash @elastic/logstash -/x-pack/platform/test/api_integration/apis/logstash @elastic/logstash #CC# /x-pack/platform/plugins/private/logstash/ @elastic/logstash # EUI team diff --git a/src/platform/test/moon.yml b/src/platform/test/moon.yml index 266d00443497f..a4f3a8c2724ef 100644 --- a/src/platform/test/moon.yml +++ b/src/platform/test/moon.yml @@ -81,8 +81,6 @@ tags: fileGroups: src: - '**/*' - - api_integration/apis/logstash/pipeline/fixtures/*.json - - api_integration/apis/logstash/pipelines/fixtures/*.json - api_integration/apis/telemetry/fixtures/*.json - api_integration/apis/telemetry/fixtures/*.json - '!target/**/*' diff --git a/src/platform/test/tsconfig.json b/src/platform/test/tsconfig.json index d2794663cc4a3..fb60c17e22131 100644 --- a/src/platform/test/tsconfig.json +++ b/src/platform/test/tsconfig.json @@ -14,8 +14,6 @@ "**/*", "../../../typings/**/*", "../../../src/platform/packages/shared/kbn-test/types/ftr_globals/**/*", - "api_integration/apis/logstash/pipeline/fixtures/*.json", - "api_integration/apis/logstash/pipelines/fixtures/*.json", "api_integration/apis/telemetry/fixtures/*.json", "api_integration/apis/telemetry/fixtures/*.json" , "../../../x-pack/platform/test/serverless/functional/test_suites/saved_objects_management/export_transform copy.ts" ], diff --git a/x-pack/platform/plugins/private/logstash/moon.yml b/x-pack/platform/plugins/private/logstash/moon.yml index ffe6d04c05fdf..e186bb8621167 100644 --- a/x-pack/platform/plugins/private/logstash/moon.yml +++ b/x-pack/platform/plugins/private/logstash/moon.yml @@ -30,6 +30,7 @@ dependsOn: - '@kbn/code-editor' - '@kbn/react-kibana-context-render' - '@kbn/core-plugins-browser' + - '@kbn/scout' tags: - plugin - prod @@ -41,6 +42,7 @@ fileGroups: - common/**/* - public/**/* - server/**/* + - test/scout/**/* - '!target/**/*' jest-config: - jest.config.js diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/constants.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/constants.ts new file mode 100644 index 0000000000000..2680ea5022aba --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/constants.ts @@ -0,0 +1,48 @@ +/* + * 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 { ElasticsearchRoleDescriptor } from '@kbn/scout'; + +export const API_PATHS = { + CLUSTER: 'api/logstash/cluster', + PIPELINE: (id: string) => `api/logstash/pipeline/${id}`, + PIPELINES: 'api/logstash/pipelines', + PIPELINES_DELETE: 'api/logstash/pipelines/delete', +}; + +export const COMMON_HEADERS = { + 'kbn-xsrf': 'scout', + 'x-elastic-internal-origin': 'kibana', +}; + +export const PIPELINE_IDS = { + TWEETS_AND_BEATS: 'tweets_and_beats', + FAST_GENERATOR: 'fast_generator', + /** Unique IDs for the bulk-delete test */ + BULK_DELETE_1: 'scout_bulk_delete_1', + BULK_DELETE_2: 'scout_bulk_delete_2', +}; + +/** Expected response body for GET /api/logstash/pipeline/tweets_and_beats */ +export const EXPECTED_TWEETS_AND_BEATS_PIPELINE = { + id: 'tweets_and_beats', + description: 'ingest tweets and beats', + username: 'elastic', + pipeline: + 'input {\n twitter {\n consumer_key => "enter_your_consumer_key_here"\n consumer_secret => "enter_your_secret_here"\n keywords => ["cloud"]\n oauth_token => "enter_your_access_token_here"\n oauth_token_secret => "enter_your_access_token_secret_here"\n }\n beats {\n port => "5043"\n }\n}\noutput {\n elasticsearch {\n hosts => ["IP Address 1:port1", "IP Address 2:port2", "IP Address 3"]\n }\n}', + settings: {}, +}; + +/** Minimum ES privileges required to manage Logstash pipelines */ +export const LOGSTASH_MANAGER_ROLE: ElasticsearchRoleDescriptor = { + cluster: ['manage_logstash_pipelines'], +}; + +/** Minimum ES privileges required to call GET /api/logstash/cluster (proxies ES info()) */ +export const LOGSTASH_CLUSTER_ROLE: ElasticsearchRoleDescriptor = { + cluster: ['monitor'], +}; diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/index.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/index.ts new file mode 100644 index 0000000000000..56039e8ecbc59 --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/fixtures/index.ts @@ -0,0 +1,28 @@ +/* + * 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 { apiTest as base } from '@kbn/scout'; +import { getLogstashApiService, type LogstashApiService } from '../services/logstash_api_service'; + +// Augment the shared ApiServicesFixture interface so that apiServices.logstash is typed +// everywhere inside this plugin's Scout tests. +declare module '@kbn/scout' { + interface ApiServicesFixture { + logstash: LogstashApiService; + } +} + +export const apiTest = base.extend<{}, { apiServices: import('@kbn/scout').ApiServicesFixture }>({ + apiServices: [ + async ({ apiServices, esClient, log }, use) => { + await use(Object.assign(apiServices, { logstash: getLogstashApiService({ esClient, log }) })); + }, + { scope: 'worker' }, + ], +}); + +export * as testData from './constants'; diff --git a/x-pack/platform/test/api_integration/apis/logstash/cluster/index.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/playwright.config.ts similarity index 53% rename from x-pack/platform/test/api_integration/apis/logstash/cluster/index.ts rename to x-pack/platform/plugins/private/logstash/test/scout/api/playwright.config.ts index 72f3de33aea0e..75a7694d12043 100644 --- a/x-pack/platform/test/api_integration/apis/logstash/cluster/index.ts +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/playwright.config.ts @@ -5,10 +5,8 @@ * 2.0. */ -import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { createPlaywrightConfig } from '@kbn/scout'; -export default function ({ loadTestFile }: FtrProviderContext) { - describe('cluster', () => { - loadTestFile(require.resolve('./load')); - }); -} +export default createPlaywrightConfig({ + testDir: './tests', +}); diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/services/logstash_api_service.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/services/logstash_api_service.ts new file mode 100644 index 0000000000000..8c0eec24e9963 --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/services/logstash_api_service.ts @@ -0,0 +1,63 @@ +/* + * 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 { ScoutLogger } from '@kbn/scout'; +import type { Client } from '@elastic/elasticsearch'; + +export interface LogstashApiService { + /** + * Creates one or more minimal Logstash pipelines by ID via the ES Logstash Pipeline Management API. + */ + createPipelines: (...ids: string[]) => Promise; + /** + * Deletes one or more Logstash pipelines by ID using the ES Logstash Pipeline Management API. + * Silently ignores 404s so it is safe to call in afterAll even when setup failed partway through. + */ + deletePipelines: (...ids: string[]) => Promise; +} + +// ES accepts an empty settings object at runtime; the TS type is overly strict +type PipelineSettings = import('@elastic/elasticsearch').estypes.LogstashPipelineSettings; +const EMPTY_PIPELINE_SETTINGS = {} as unknown as PipelineSettings; + +export const getLogstashApiService = ({ + log, + esClient, +}: { + log: ScoutLogger; + esClient: Client; +}): LogstashApiService => { + return { + createPipelines: async (...ids: string[]) => { + for (const id of ids) { + log.debug(`[logstash] Creating pipeline '${id}'`); + await esClient.logstash.putPipeline({ + id, + pipeline: { + description: `pipeline ${id}`, + last_modified: new Date().toISOString(), + pipeline: '# empty', + pipeline_metadata: { type: 'logstash_pipeline', version: '1' }, + pipeline_settings: EMPTY_PIPELINE_SETTINGS, + username: 'elastic', + }, + }); + } + }, + + deletePipelines: async (...ids: string[]) => { + for (const id of ids) { + log.debug(`[logstash] Deleting pipeline '${id}'`); + await esClient.logstash.deletePipeline({ id }).catch((err) => { + if (err?.statusCode !== 404) { + log.warning(`[logstash] Failed to delete pipeline '${id}': ${err?.message}`); + } + }); + } + }, + }; +}; diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/tests/cluster.spec.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/cluster.spec.ts new file mode 100644 index 0000000000000..baae2329f6577 --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/cluster.spec.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { apiTest, testData } from '../fixtures'; + +// The route calls client.asCurrentUser.info() which requires the ES `monitor` cluster privilege. +// The built-in `viewer` Kibana role has no ES cluster privileges and produces a non-403 error +// that bypasses the route's catch block, resulting in a 500. A custom role with `monitor` is +// the minimum required privilege. +apiTest.describe('GET /api/logstash/cluster', { tag: tags.stateful.classic }, () => { + let credentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth }) => { + credentials = await requestAuth.getApiKeyForCustomRole(testData.LOGSTASH_CLUSTER_ROLE); + }); + + apiTest('should return the ES cluster info', async ({ apiClient, esClient }) => { + const response = await apiClient.get(testData.API_PATHS.CLUSTER, { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.cluster).toBeDefined(); + + const esInfo = await esClient.info(); + expect(response.body.cluster.uuid).toBe(esInfo.cluster_uuid); + }); +}); diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipeline.spec.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipeline.spec.ts new file mode 100644 index 0000000000000..ab41be60a294e --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipeline.spec.ts @@ -0,0 +1,109 @@ +/* + * 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 { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { apiTest, testData } from '../fixtures'; + +apiTest.describe('/api/logstash/pipeline/{id}', { tag: tags.stateful.classic }, () => { + let credentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth, esClient }) => { + credentials = await requestAuth.getApiKeyForCustomRole(testData.LOGSTASH_MANAGER_ROLE); + + await esClient.logstash.putPipeline({ + id: testData.PIPELINE_IDS.TWEETS_AND_BEATS, + pipeline: { + description: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.description, + last_modified: '2017-08-02T18:59:07.724Z', + pipeline: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.pipeline, + pipeline_metadata: { type: 'logstash_pipeline', version: '1' }, + // ES accepts an empty settings object at runtime; the TS type is overly strict + pipeline_settings: + {} as unknown as import('@elastic/elasticsearch').estypes.LogstashPipelineSettings, + username: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.username, + }, + }); + }); + + apiTest.afterAll(async ({ apiServices }) => { + await apiServices.logstash.deletePipelines( + testData.PIPELINE_IDS.TWEETS_AND_BEATS, + testData.PIPELINE_IDS.FAST_GENERATOR + ); + }); + + apiTest('GET should return the specified pipeline', async ({ apiClient }) => { + const response = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.TWEETS_AND_BEATS), + { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + } + ); + + expect(response).toHaveStatusCode(200); + expect(response.body).toStrictEqual(testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE); + }); + + apiTest('GET should return 404 for a non-existing pipeline', async ({ apiClient }) => { + const response = await apiClient.get(testData.API_PATHS.PIPELINE('non_existing_pipeline'), { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(404); + }); + + apiTest( + 'PUT should create the specified pipeline and DELETE should remove it', + async ({ apiClient }) => { + const createResponse = await apiClient.put( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.FAST_GENERATOR), + { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + body: { + description: 'foobar baz', + pipeline: 'input { generator {} }\n\n output { stdout {} }', + }, + responseType: 'json', + } + ); + expect(createResponse).toHaveStatusCode(204); + + const loadResponse = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.FAST_GENERATOR), + { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(loadResponse).toHaveStatusCode(200); + expect(loadResponse.body.id).toBe(testData.PIPELINE_IDS.FAST_GENERATOR); + expect(loadResponse.body.description).toBe('foobar baz'); + expect(loadResponse.body.pipeline).toBe('input { generator {} }\n\n output { stdout {} }'); + + const deleteResponse = await apiClient.delete( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.FAST_GENERATOR), + { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + } + ); + expect(deleteResponse).toHaveStatusCode(204); + + const afterDeleteResponse = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.FAST_GENERATOR), + { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + } + ); + expect(afterDeleteResponse).toHaveStatusCode(404); + } + ); +}); diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_delete.spec.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_delete.spec.ts new file mode 100644 index 0000000000000..9caf708670821 --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_delete.spec.ts @@ -0,0 +1,67 @@ +/* + * 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 { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { apiTest, testData } from '../fixtures'; + +apiTest.describe('POST /api/logstash/pipelines/delete', { tag: tags.stateful.classic }, () => { + let credentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth, apiServices }) => { + credentials = await requestAuth.getApiKeyForCustomRole(testData.LOGSTASH_MANAGER_ROLE); + await apiServices.logstash.createPipelines( + testData.PIPELINE_IDS.BULK_DELETE_1, + testData.PIPELINE_IDS.BULK_DELETE_2 + ); + }); + + apiTest.afterAll(async ({ apiServices }) => { + await apiServices.logstash.deletePipelines( + testData.PIPELINE_IDS.BULK_DELETE_1, + testData.PIPELINE_IDS.BULK_DELETE_2 + ); + }); + + apiTest('should delete the specified pipelines', async ({ apiClient }) => { + const p1Before = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.BULK_DELETE_1), + { headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, responseType: 'json' } + ); + expect(p1Before).toHaveStatusCode(200); + + const p2Before = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.BULK_DELETE_2), + { headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, responseType: 'json' } + ); + expect(p2Before).toHaveStatusCode(200); + + const deleteResponse = await apiClient.post(testData.API_PATHS.PIPELINES_DELETE, { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + body: { + pipelineIds: [testData.PIPELINE_IDS.BULK_DELETE_1, testData.PIPELINE_IDS.BULK_DELETE_2], + }, + responseType: 'json', + }); + expect(deleteResponse).toHaveStatusCode(200); + expect(deleteResponse.body.results.numSuccesses).toBe(2); + expect(deleteResponse.body.results.numErrors).toBe(0); + + const p1After = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.BULK_DELETE_1), + { headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, responseType: 'json' } + ); + expect(p1After).toHaveStatusCode(404); + + const p2After = await apiClient.get( + testData.API_PATHS.PIPELINE(testData.PIPELINE_IDS.BULK_DELETE_2), + { headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, responseType: 'json' } + ); + expect(p2After).toHaveStatusCode(404); + }); +}); diff --git a/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_list.spec.ts b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_list.spec.ts new file mode 100644 index 0000000000000..22cbd8b80aa57 --- /dev/null +++ b/x-pack/platform/plugins/private/logstash/test/scout/api/tests/pipelines_list.spec.ts @@ -0,0 +1,78 @@ +/* + * 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 { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { apiTest, testData } from '../fixtures'; + +apiTest.describe('GET /api/logstash/pipelines', { tag: tags.stateful.classic }, () => { + let credentials: RoleApiCredentials; + + const EMPTY_PIPELINE_IDS = Array.from({ length: 21 }, (_, i) => `empty_pipeline_${i + 1}`); + + apiTest.beforeAll(async ({ requestAuth, esClient, apiServices }) => { + credentials = await requestAuth.getApiKeyForCustomRole(testData.LOGSTASH_MANAGER_ROLE); + + // tweets_and_beats has specific field values asserted by the test, so it + // cannot use the generic createPipelines helper defaults. + // ES accepts an empty settings object at runtime; the TS type is overly strict + type PipelineSettings = import('@elastic/elasticsearch').estypes.LogstashPipelineSettings; + await esClient.logstash.putPipeline({ + id: testData.PIPELINE_IDS.TWEETS_AND_BEATS, + pipeline: { + description: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.description, + last_modified: '2017-08-02T18:59:07.724Z', + pipeline: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.pipeline, + pipeline_metadata: { type: 'logstash_pipeline', version: '1' }, + pipeline_settings: {} as unknown as PipelineSettings, + username: testData.EXPECTED_TWEETS_AND_BEATS_PIPELINE.username, + }, + }); + + await apiServices.logstash.createPipelines(...EMPTY_PIPELINE_IDS); + }); + + apiTest.afterAll(async ({ apiServices }) => { + await apiServices.logstash.deletePipelines( + testData.PIPELINE_IDS.TWEETS_AND_BEATS, + ...EMPTY_PIPELINE_IDS + ); + }); + + apiTest('should return all the pipelines', async ({ apiClient }) => { + const response = await apiClient.get(testData.API_PATHS.PIPELINES, { + headers: { ...testData.COMMON_HEADERS, ...credentials.apiKeyHeader }, + responseType: 'json', + }); + + expect(response).toHaveStatusCode(200); + expect(response.body.pipelines).toHaveLength(22); + + // Every entry must carry the expected shape + for (const pipeline of response.body.pipelines as Array>) { + expect(typeof pipeline.id).toBe('string'); + expect(typeof pipeline.description).toBe('string'); + expect(typeof pipeline.last_modified).toBe('string'); + expect(typeof pipeline.username).toBe('string'); + } + + // Verify results are sorted alphabetically by id (as implemented in the route) + const ids = (response.body.pipelines as Array<{ id: string }>).map((p) => p.id); + expect(ids).toStrictEqual([...ids].sort()); + + // Spot-check known pipeline + const tweetsAndBeats = (response.body.pipelines as Array>).find( + (p) => p.id === testData.PIPELINE_IDS.TWEETS_AND_BEATS + ); + expect(tweetsAndBeats).toMatchObject({ + id: 'tweets_and_beats', + description: 'ingest tweets and beats', + username: 'elastic', + }); + }); +}); diff --git a/x-pack/platform/plugins/private/logstash/tsconfig.json b/x-pack/platform/plugins/private/logstash/tsconfig.json index ba2a796cf184a..2d47db1f93322 100644 --- a/x-pack/platform/plugins/private/logstash/tsconfig.json +++ b/x-pack/platform/plugins/private/logstash/tsconfig.json @@ -1,14 +1,9 @@ - { "extends": "@kbn/tsconfig-base/tsconfig.json", "compilerOptions": { - "outDir": "target/types", + "outDir": "target/types" }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - ], + "include": ["common/**/*", "public/**/*", "server/**/*", "test/scout/**/*"], "kbn_references": [ "@kbn/core", "@kbn/home-plugin", @@ -24,8 +19,7 @@ "@kbn/code-editor", "@kbn/react-kibana-context-render", "@kbn/core-plugins-browser", + "@kbn/scout" ], - "exclude": [ - "target/**/*", - ] + "exclude": ["target/**/*"] } diff --git a/x-pack/platform/test/api_integration/apis/logstash/cluster/load.ts b/x-pack/platform/test/api_integration/apis/logstash/cluster/load.ts deleted file mode 100644 index 3da97b19f3ff0..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/cluster/load.ts +++ /dev/null @@ -1,23 +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'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const es = getService('es'); - - describe('load', () => { - it('should return the ES cluster info', async () => { - const { body } = await supertest.get('/api/logstash/cluster').expect(200); - - const responseFromES = await es.info(); - expect(body.cluster.uuid).to.eql(responseFromES.cluster_uuid); - }); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/config.ts b/x-pack/platform/test/api_integration/apis/logstash/config.ts deleted file mode 100644 index 73613f45a7692..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/config.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 { FtrConfigProviderContext } from '@kbn/test'; - -export default async function ({ readConfigFile }: FtrConfigProviderContext) { - const baseIntegrationTestsConfig = await readConfigFile(require.resolve('../../config.ts')); - - return { - ...baseIntegrationTestsConfig.getAll(), - testFiles: [require.resolve('.')], - }; -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/index.ts b/x-pack/platform/test/api_integration/apis/logstash/index.ts deleted file mode 100644 index aacd1b30d9dec..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/index.ts +++ /dev/null @@ -1,16 +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('logstash', () => { - loadTestFile(require.resolve('./pipelines')); - loadTestFile(require.resolve('./pipeline')); - loadTestFile(require.resolve('./cluster')); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipeline/fixtures/load.json b/x-pack/platform/test/api_integration/apis/logstash/pipeline/fixtures/load.json deleted file mode 100644 index 1b724d24b0cfb..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipeline/fixtures/load.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "tweets_and_beats", - "description": "ingest tweets and beats", - "username": "elastic", - "pipeline": "input {\n twitter {\n consumer_key => \"enter_your_consumer_key_here\"\n consumer_secret => \"enter_your_secret_here\"\n keywords => [\"cloud\"]\n oauth_token => \"enter_your_access_token_here\"\n oauth_token_secret => \"enter_your_access_token_secret_here\"\n }\n beats {\n port => \"5043\"\n }\n}\noutput {\n elasticsearch {\n hosts => [\"IP Address 1:port1\", \"IP Address 2:port2\", \"IP Address 3\"]\n }\n}", - "settings": {} -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipeline/index.ts b/x-pack/platform/test/api_integration/apis/logstash/pipeline/index.ts deleted file mode 100644 index a15481881c56c..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipeline/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('pipeline', () => { - loadTestFile(require.resolve('./load')); - loadTestFile(require.resolve('./save')); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipeline/load.ts b/x-pack/platform/test/api_integration/apis/logstash/pipeline/load.ts deleted file mode 100644 index bbe41b40d975c..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipeline/load.ts +++ /dev/null @@ -1,33 +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 pipeline from './fixtures/load.json'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('list', () => { - const archive = 'x-pack/platform/test/fixtures/es_archives/logstash/example_pipelines'; - - before('load pipelines archive', () => { - return esArchiver.load(archive); - }); - - after('unload pipelines archive', () => { - return esArchiver.unload(archive); - }); - - it('should return the specified pipeline', async () => { - const { body } = await supertest.get('/api/logstash/pipeline/tweets_and_beats').expect(200); - - expect(body).to.eql(pipeline); - }); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipeline/save.ts b/x-pack/platform/test/api_integration/apis/logstash/pipeline/save.ts deleted file mode 100644 index 476f73ce74ce7..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipeline/save.ts +++ /dev/null @@ -1,39 +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'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('save', () => { - after('delete created pipeline', async () => { - await supertest - .delete('/api/logstash/pipeline/fast_generator') - .set('kbn-xsrf', 'xxx') - .expect(204); - - await supertest.get('/api/logstash/pipeline/fast_generator').expect(404); - }); - - it('should create the specified pipeline', async () => { - await supertest - .put('/api/logstash/pipeline/fast_generator') - .set('kbn-xsrf', 'xxx') - .send({ - description: 'foobar baz', - pipeline: 'input { generator {} }\n\n output { stdout {} }', - }) - .expect(204); - - const { body } = await supertest.get('/api/logstash/pipeline/fast_generator').expect(200); - - expect(body.description).to.eql('foobar baz'); - }); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipelines/delete.ts b/x-pack/platform/test/api_integration/apis/logstash/pipelines/delete.ts deleted file mode 100644 index 639dfee5477bf..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipelines/delete.ts +++ /dev/null @@ -1,39 +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 ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('delete', () => { - const archive = 'x-pack/platform/test/fixtures/es_archives/logstash/example_pipelines'; - - before('load pipelines archive', async () => { - await esArchiver.load(archive); - await supertest.get('/api/logstash/pipeline/empty_pipeline_1').expect(200); - await supertest.get('/api/logstash/pipeline/empty_pipeline_2').expect(200); - }); - - after('unload pipelines archive', () => { - return esArchiver.unload(archive); - }); - - it('should delete the specified pipelines', async () => { - await supertest - .post('/api/logstash/pipelines/delete') - .set('kbn-xsrf', 'xxx') - .send({ - pipelineIds: ['empty_pipeline_1', 'empty_pipeline_2'], - }) - .expect(200); - - await supertest.get('/api/logstash/pipeline/empty_pipeline_1').expect(404); - await supertest.get('/api/logstash/pipeline/empty_pipeline_2').expect(404); - }); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipelines/fixtures/list.json b/x-pack/platform/test/api_integration/apis/logstash/pipelines/fixtures/list.json deleted file mode 100644 index b4c973f22ca28..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipelines/fixtures/list.json +++ /dev/null @@ -1,136 +0,0 @@ -{ - "pipelines": [ - { - "id": "empty_pipeline_1", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_10", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_11", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_12", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_13", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_14", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_15", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_16", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_17", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_18", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_19", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_2", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_20", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_21", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_3", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_4", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_5", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_6", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_7", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_8", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "empty_pipeline_9", - "description": "an empty pipeline", - "last_modified": "2017-08-02T18:57:32.907Z", - "username": "elastic" - }, - { - "id": "tweets_and_beats", - "description": "ingest tweets and beats", - "last_modified": "2017-08-02T18:59:07.724Z", - "username": "elastic" - } - ] -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipelines/index.ts b/x-pack/platform/test/api_integration/apis/logstash/pipelines/index.ts deleted file mode 100644 index 6195f9a5ab53a..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipelines/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('pipelines', () => { - loadTestFile(require.resolve('./list')); - loadTestFile(require.resolve('./delete')); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/logstash/pipelines/list.ts b/x-pack/platform/test/api_integration/apis/logstash/pipelines/list.ts deleted file mode 100644 index 6f6b869cbe4ec..0000000000000 --- a/x-pack/platform/test/api_integration/apis/logstash/pipelines/list.ts +++ /dev/null @@ -1,33 +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 pipelineList from './fixtures/list.json'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - // Failing: See https://github.com/elastic/kibana/issues/151350 - describe.skip('list', () => { - const archive = 'x-pack/platform/test/fixtures/es_archives/logstash/example_pipelines'; - - before('load pipelines archive', () => { - return esArchiver.load(archive); - }); - - after('unload pipelines archive', () => { - return esArchiver.unload(archive); - }); - - it('should return all the pipelines', async () => { - const { body } = await supertest.get('/api/logstash/pipelines').expect(200); - - expect(body).to.eql(pipelineList); - }); - }); -} diff --git a/x-pack/platform/test/moon.yml b/x-pack/platform/test/moon.yml index d3afc84141586..f51ee3435361b 100644 --- a/x-pack/platform/test/moon.yml +++ b/x-pack/platform/test/moon.yml @@ -178,8 +178,6 @@ fileGroups: src: - index.d.ts - '**/*' - - ./api_integration/apis/logstash/pipeline/fixtures/*.json - - ./api_integration/apis/logstash/pipelines/fixtures/*.json - ./api_integration/apis/telemetry/fixtures/*.json - ./monitoring_api_integration/fixtures/**/*.json - '!target/**/*' diff --git a/x-pack/platform/test/tsconfig.json b/x-pack/platform/test/tsconfig.json index 4fb3dd8935c2f..fa9684928f180 100644 --- a/x-pack/platform/test/tsconfig.json +++ b/x-pack/platform/test/tsconfig.json @@ -21,8 +21,6 @@ "../../../typings/**/*", "../../../src/platform/packages/shared/kbn-test/types/ftr_globals/**/*", "../../*.json", - "./api_integration/apis/logstash/pipeline/fixtures/*.json", - "./api_integration/apis/logstash/pipelines/fixtures/*.json", "./api_integration/apis/telemetry/fixtures/*.json", "./monitoring_api_integration/fixtures/**/*.json" ],