From 8629b1da8e392880632b197b4d5b031353a39f28 Mon Sep 17 00:00:00 2001 From: Sonia Sanz Vivas Date: Wed, 27 Nov 2024 19:50:35 +0100 Subject: [PATCH] Allow data Streams enrich policies (#200903) Closes [#187438](https://github.com/elastic/kibana/issues/187438) ## Summary This PR introduces the data streams as source indices in the enrich policies. For that, it uses the groups functionality, so the indices are separated from the data streams. https://github.com/user-attachments/assets/1716d57b-b8c7-417a-b75c-33378f3269ca ### Strings I've changed some of the existing strings but left other as they were. I'll be happy to also change those if folks feel that should be the case. I've added a hint text and changed the label for the box in the creation step (marked in yellow) Screenshot 2024-11-20 at 12 35 32 I haven't change the label for the indices group (green) and I've followed the same structure for the new group category (yellow)
Screenshot 2024-11-20 at 12 37 51 Screenshot 2024-11-20 at 12 38 16
I haven't change the 'Source indices' text in the Summary (green) because in the request tab the correspondan field in the request is indices (blue)
Screenshot 2024-11-20 at 12 38 53 Screenshot 2024-11-20 at 12 39 19
I also haven't change the 'Source indices' text in the table or in the detail flyout (green) since both indices and data streams are treated like indices.
Screenshot 2024-11-20 at 12 39 52 Screenshot 2024-11-20 at 12 40 25
### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_node:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../create_enrich_policy.test.tsx | 7 +- .../client_integration/helpers/fixtures.ts | 3 + .../helpers/http_requests.ts | 8 ++ .../steps/configuration.tsx | 13 ++- .../steps/fields/indices_selector.tsx | 82 +++++++++++++++---- .../public/application/services/api.ts | 10 +++ .../enrich_policies/enrich_policies.test.ts | 29 +++++++ .../routes/api/enrich_policies/helpers.ts | 12 +++ .../enrich_policies/register_create_route.ts | 35 +++++++- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../index_management/create_enrich_policy.ts | 59 ++++++++++++- 13 files changed, 230 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx index 9332509dc26b9..a9675b592aa39 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx @@ -9,7 +9,11 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { setupEnvironment } from '../helpers'; -import { getMatchingIndices, getFieldsFromIndices } from '../helpers/fixtures'; +import { + getMatchingIndices, + getFieldsFromIndices, + getMatchingDataStreams, +} from '../helpers/fixtures'; import { CreateEnrichPoliciesTestBed, setup } from './create_enrich_policy.helpers'; import { getESPolicyCreationApiCall } from '../../../common/lib'; @@ -57,6 +61,7 @@ describe('Create enrich policy', () => { hasAllPrivileges: true, missingPrivileges: { cluster: [] }, }); + httpRequestsMockHelpers.setGetMatchingDataStreams(getMatchingDataStreams()); await act(async () => { testBed = await setup(httpSetup); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts index a70ad0ea552ec..0741b44495600 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/fixtures.ts @@ -62,6 +62,9 @@ export const createTestEnrichPolicy = (name: string, type: EnrichPolicyType) => export const getMatchingIndices = () => ({ indices: ['test-1', 'test-2', 'test-3', 'test-4', 'test-5'], }); +export const getMatchingDataStreams = () => ({ + dataStreams: ['test-6', 'test-7', 'test-8', 'test-9', 'test-10'], +}); export const getFieldsFromIndices = () => ({ commonFields: [], diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts index 79daba4c73867..0504f9bf40b7a 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -172,6 +172,13 @@ const registerHttpRequestMockHelpers = ( response, error ); + const setGetMatchingDataStreams = (response?: HttpResponse, error?: ResponseError) => + mockResponse( + 'POST', + `${INTERNAL_API_BASE_PATH}/enrich_policies/get_matching_data_streams`, + response, + error + ); const setGetFieldsFromIndices = (response?: HttpResponse, error?: ResponseError) => mockResponse( @@ -249,6 +256,7 @@ const registerHttpRequestMockHelpers = ( setGetPrivilegesResponse, setCreateEnrichPolicy, setInferenceModels, + setGetMatchingDataStreams, }; }; diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/configuration.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/configuration.tsx index 6e743c6bd0781..175bb812dbd5f 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/configuration.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/configuration.tsx @@ -94,18 +94,15 @@ export const configurationFormSchema: FormSchema = { }, sourceIndices: { - label: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesLabel', { - defaultMessage: 'Source indices', + label: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceLabel', { + defaultMessage: 'Source', }), validations: [ { validator: fieldValidators.emptyField( - i18n.translate( - 'xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesRequiredError', - { - defaultMessage: 'At least one source index is required.', - } - ) + i18n.translate('xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceRequiredError', { + defaultMessage: 'At least one source is required.', + }) ), }, ], diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/fields/indices_selector.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/fields/indices_selector.tsx index 9181449f2dbac..825a977638417 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/fields/indices_selector.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/steps/fields/indices_selector.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { uniq, isEmpty } from 'lodash'; import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import type { EuiComboBoxProps } from '@elastic/eui'; -import { getMatchingIndices } from '../../../../services/api'; +import { getMatchingDataStreams, getMatchingIndices } from '../../../../services/api'; import type { FieldHook } from '../../../../../shared_imports'; import { getFieldValidityAndErrorMessage } from '../../../../../shared_imports'; @@ -25,23 +25,76 @@ interface Props { [key: string]: any; } -const getIndexOptions = async (patternString: string) => { - const options: IOption[] = []; +interface GetMatchingOptionsParams { + matches: string[]; + optionsMessage: string; + noMatchingMessage: string; +} +const i18nTexts = { + indices: { + options: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.indicesSelector.optionsLabel', { + defaultMessage: 'Based on your indices', + }), + noMatches: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.indicesSelector.noMatchingOption', { + defaultMessage: 'No indices match your search criteria.', + }), + }, + dataStreams: { + options: i18n.translate( + 'xpack.idxMgmt.enrichPolicyCreate.indicesSelector.dataStream.optionsLabel', + { + defaultMessage: 'Based on your data streams', + } + ), + noMatches: i18n.translate( + 'xpack.idxMgmt.enrichPolicyCreate.indicesSelector.dataStream.noMatchingOption', + { + defaultMessage: 'No data streams match your search criteria.', + } + ), + }, + sourcePlaceholder: i18n.translate( + 'xpack.idxMgmt.enrichPolicyCreate.indicesSelector.placeholder', + { + defaultMessage: 'Select source indices and data streams.', + } + ), +}; + +const getIndexOptions = async (patternString: string) => { if (!patternString) { - return options; + return []; } - const { data } = await getMatchingIndices(patternString); - const matchingIndices = data.indices; + const { data: indicesData } = await getMatchingIndices(patternString); + const { data: dataStreamsData } = await getMatchingDataStreams(patternString); - if (matchingIndices.length) { - const matchingOptions = uniq([...matchingIndices]); + const indices = getMatchingOptions({ + matches: indicesData.indices, + optionsMessage: i18nTexts.indices.options, + noMatchingMessage: i18nTexts.indices.noMatches, + }); + const dataStreams = getMatchingOptions({ + matches: dataStreamsData.dataStreams, + optionsMessage: i18nTexts.dataStreams.options, + noMatchingMessage: i18nTexts.dataStreams.noMatches, + }); + + return [...indices, ...dataStreams]; +}; + +const getMatchingOptions = ({ + matches, + optionsMessage, + noMatchingMessage, +}: GetMatchingOptionsParams) => { + const options: IOption[] = []; + if (matches.length) { + const matchingOptions = uniq([...matches]); options.push({ - label: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.indicesSelector.optionsLabel', { - defaultMessage: 'Based on your indices', - }), + label: optionsMessage, options: matchingOptions .map((match) => { return { @@ -53,13 +106,10 @@ const getIndexOptions = async (patternString: string) => { }); } else { options.push({ - label: i18n.translate('xpack.idxMgmt.enrichPolicyCreate.indicesSelector.noMatchingOption', { - defaultMessage: 'No indices match your search criteria.', - }), + label: noMatchingMessage, options: [], }); } - return options; }; @@ -71,7 +121,6 @@ export const IndicesSelector = ({ field, euiFieldProps, ...rest }: Props) => { const onSearchChange = useCallback( async (search: string) => { const indexPattern = isEmpty(search) ? '*' : search; - setIsIndiciesLoading(true); setIndexOptions(await getIndexOptions(indexPattern)); setIsIndiciesLoading(false); @@ -98,6 +147,7 @@ export const IndicesSelector = ({ field, euiFieldProps, ...rest }: Props) => { > ({ path: `${INTERNAL_API_BASE_PATH}/enrich_policies/get_fields_from_indices`, diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/enrich_policies.test.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/enrich_policies.test.ts index fb548b49623ee..98a9c7f34d018 100644 --- a/x-pack/plugins/index_management/server/routes/api/enrich_policies/enrich_policies.test.ts +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/enrich_policies.test.ts @@ -366,4 +366,33 @@ describe('Enrich policies API', () => { expect(searchMock).toHaveBeenCalled(); }); }); + + describe('Get matching indices - POST /api/index_management/enrich_policies/get_matching_data_streams', () => { + const getDataStreamsMock = router.getMockESApiFn('indices.getDataStream'); + + it('Return matching data streams', async () => { + const mockRequest: RequestMock = { + method: 'post', + path: addInternalBasePath('/enrich_policies/get_matching_data_streams'), + body: { + pattern: 'test', + }, + }; + + getDataStreamsMock.mockResolvedValue({ + body: {}, + statusCode: 200, + }); + + const res = await router.runRequest(mockRequest); + + expect(res).toEqual({ + body: { + dataStreams: [], + }, + }); + + expect(getDataStreamsMock).toHaveBeenCalledWith({ name: '*test*', expand_wildcards: 'open' }); + }); + }); }); diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/helpers.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/helpers.ts index 6ecc6e9eb7ade..32b7d0b64718c 100644 --- a/x-pack/plugins/index_management/server/routes/api/enrich_policies/helpers.ts +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/helpers.ts @@ -145,3 +145,15 @@ export async function getIndices(dataClient: IScopedClusterClient, pattern: stri return indices.buckets ? indices.buckets.map((bucket) => bucket.key) : []; } +export async function getDataStreams( + dataClient: IScopedClusterClient, + pattern: string, + limit = 10 +) { + const response = await dataClient.asCurrentUser.indices.getDataStream({ + name: pattern, + expand_wildcards: 'open', + }); + + return response.data_streams ? response.data_streams.map((dataStream) => dataStream.name) : []; +} diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts index bb6ef8b1fff50..ff165876a6ee9 100644 --- a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_create_route.ts @@ -13,7 +13,13 @@ import { RouteDependencies } from '../../../types'; import { addInternalBasePath } from '..'; import { enrichPoliciesActions } from '../../../lib/enrich_policies'; import { serializeAsESPolicy } from '../../../../common/lib'; -import { normalizeFieldsList, getIndices, FieldCapsList, getCommonFields } from './helpers'; +import { + normalizeFieldsList, + getIndices, + FieldCapsList, + getCommonFields, + getDataStreams, +} from './helpers'; const validationSchema = schema.object({ policy: schema.object({ @@ -105,6 +111,33 @@ export function registerCreateRoute({ router, lib: { handleEsError } }: RouteDep } } ); + router.post( + { + path: addInternalBasePath('/enrich_policies/get_matching_data_streams'), + validate: { body: getMatchingIndicesSchema }, + }, + async (context, request, response) => { + let { pattern } = request.body; + const client = (await context.core).elasticsearch.client as IScopedClusterClient; + + // Add wildcards to the search query to match the behavior of the + // index pattern search in the Kibana UI. + if (!pattern.startsWith('*')) { + pattern = `*${pattern}`; + } + if (!pattern.endsWith('*')) { + pattern = `${pattern}*`; + } + + try { + const dataStreams = await getDataStreams(client, pattern); + + return response.ok({ body: { dataStreams } }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ); router.post( { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 765be17d98b5d..0d443d929fc0a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -22567,8 +22567,6 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryLabel": "Requête (facultative)", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeOption": "Plage", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type} correspond à un nombre, une date ou une plage d'adresses IP.", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesLabel": "Index source", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesRequiredError": "Au moins un index source est requis.", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeRequiredError": "Une valeur est requise pour le type de politique.", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeTitlePopOver": "Détermine comment faire correspondre les données avec les documents entrants.", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.uploadFileLink": "Charger un fichier", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3f1b10bdce8ae..f74354a42c117 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22538,8 +22538,6 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryLabel": "クエリー(任意)", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeOption": "Range", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type}は、番号、日付、またはIPアドレスの範囲と一致します。", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesLabel": "ソースインデックス", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesRequiredError": "ソースインデックスが最低1つ必要です。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeRequiredError": "ポリシータイプ値が必要です。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeTitlePopOver": "どのようにデータを受信ドキュメントに一致させるかを決定します。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.uploadFileLink": "ファイルをアップロード", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 463906b3e6cff..3457f35ee34ce 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22148,8 +22148,6 @@ "xpack.idxMgmt.enrichPolicyCreate.configurationStep.queryLabel": "查询(可选)", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeOption": "范围", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.rangeTypePopOver": "{type} 匹配一个数字、日期或 IP 地址范围。", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesLabel": "源索引", - "xpack.idxMgmt.enrichPolicyCreate.configurationStep.sourceIndicesRequiredError": "至少需要一个源索引。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeRequiredError": "策略类型值必填。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.typeTitlePopOver": "确定如何将数据匹配到传入文档。", "xpack.idxMgmt.enrichPolicyCreate.configurationStep.uploadFileLink": "上传文件", diff --git a/x-pack/test/api_integration/apis/management/index_management/create_enrich_policy.ts b/x-pack/test/api_integration/apis/management/index_management/create_enrich_policy.ts index e2bfa9cdb35ef..36f5e17e347d0 100644 --- a/x-pack/test/api_integration/apis/management/index_management/create_enrich_policy.ts +++ b/x-pack/test/api_integration/apis/management/index_management/create_enrich_policy.ts @@ -19,6 +19,9 @@ export default function ({ getService }: FtrProviderContext) { const INDEX_A_NAME = `index-${Math.random()}`; const INDEX_B_NAME = `index-${Math.random()}`; const POLICY_NAME = `policy-${Math.random()}`; + const DATA_STREAM_TEMPLATE = `data-stream-template`; + const DATA_STREAM_A_NAME = `data-stream-${Math.random()}`; + const DATA_STREAM_B_NAME = `data-stream-${Math.random()}`; before(async () => { try { @@ -56,6 +59,24 @@ export default function ({ getService }: FtrProviderContext) { log.debug('[Setup error] Error creating test index'); throw err; } + try { + await es.indices.putIndexTemplate({ + name: DATA_STREAM_TEMPLATE, + body: { + index_patterns: ['data-stream-*'], + data_stream: {}, + }, + }); + await es.indices.createDataStream({ + name: DATA_STREAM_A_NAME, + }); + await es.indices.createDataStream({ + name: DATA_STREAM_B_NAME, + }); + } catch (err) { + log.debug('[Setup error] Error creating test data stream'); + throw err; + } }); after(async () => { @@ -66,6 +87,14 @@ export default function ({ getService }: FtrProviderContext) { log.debug('[Cleanup error] Error deleting test index'); throw err; } + try { + await es.indices.deleteDataStream({ name: DATA_STREAM_A_NAME }); + await es.indices.deleteDataStream({ name: DATA_STREAM_B_NAME }); + await es.indices.deleteIndexTemplate({ name: DATA_STREAM_TEMPLATE }); + } catch (err) { + log.debug('[Cleanup error] Error deleting test data stream'); + throw err; + } }); it('Allows to create an enrich policy', async () => { @@ -92,11 +121,14 @@ export default function ({ getService }: FtrProviderContext) { .post(`${INTERNAL_API_BASE_PATH}/enrich_policies/get_fields_from_indices`) .set('kbn-xsrf', 'xxx') .set('x-elastic-internal-origin', 'xxx') - .send({ indices: [INDEX_A_NAME, INDEX_B_NAME] }) + .send({ indices: [INDEX_A_NAME, INDEX_B_NAME, DATA_STREAM_A_NAME, DATA_STREAM_B_NAME] }) .expect(200); expect(body).toStrictEqual({ - commonFields: [{ name: 'email', type: 'text', normalizedType: 'text' }], + commonFields: [ + { name: 'email', type: 'text', normalizedType: 'text' }, + { name: '@timestamp', type: 'date', normalizedType: 'date' }, + ], indices: [ { index: INDEX_A_NAME, @@ -112,6 +144,14 @@ export default function ({ getService }: FtrProviderContext) { { name: 'email', type: 'text', normalizedType: 'text' }, ], }, + { + index: DATA_STREAM_A_NAME, + fields: [{ name: '@timestamp', type: 'date', normalizedType: 'date' }], + }, + { + index: DATA_STREAM_B_NAME, + fields: [{ name: '@timestamp', type: 'date', normalizedType: 'date' }], + }, ], }); }); @@ -128,5 +168,20 @@ export default function ({ getService }: FtrProviderContext) { body.indices.every((value: string) => [INDEX_A_NAME, INDEX_B_NAME].includes(value)) ).toBe(true); }); + + it('Can retrieve matching data streams', async () => { + const { body } = await supertest + .post(`${INTERNAL_API_BASE_PATH}/enrich_policies/get_matching_data_streams`) + .set('kbn-xsrf', 'xxx') + .set('x-elastic-internal-origin', 'xxx') + .send({ pattern: 'data-stream-' }) + .expect(200); + + expect( + body.dataStreams.every((value: string) => + [DATA_STREAM_A_NAME, DATA_STREAM_B_NAME].includes(value) + ) + ).toBe(true); + }); }); }