From f25fa79cf1e080dcc4d56a3ce044b8ae9b92821c Mon Sep 17 00:00:00 2001 From: Yan Savitski Date: Thu, 5 Dec 2024 20:13:37 +0100 Subject: [PATCH] [8.17] [Search] [Onboarding] Update example data for index (#201983) (#203142) # Backport This will backport the following commits from `main` to `8.17`: - [[Search] [Onboarding] Update example data for index (#201983)](https://github.com/elastic/kibana/pull/201983) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) --- .../add_documents_code_example.test.tsx | 132 ++++++++++++++++++ .../add_documents_code_example.tsx | 27 +++- .../svl_search_index_detail_page.ts | 27 ++++ 3 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.test.tsx diff --git a/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.test.tsx b/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.test.tsx new file mode 100644 index 0000000000000..c5fdc7428e690 --- /dev/null +++ b/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.test.tsx @@ -0,0 +1,132 @@ +/* + * 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 React from 'react'; +import { render } from '@testing-library/react'; +import { + AddDocumentsCodeExample, + basicExampleTexts, + exampleTextsWithCustomMapping, +} from './add_documents_code_example'; +import { generateSampleDocument } from '../../utils/document_generation'; +import { MappingProperty } from '@elastic/elasticsearch/lib/api/types'; + +jest.mock('../../utils/language', () => ({ + getDefaultCodingLanguage: jest.fn().mockReturnValue('python'), +})); + +jest.mock('../../hooks/use_asset_base_path', () => ({ + useAssetBasePath: jest.fn().mockReturnValue('/plugins/'), +})); + +jest.mock('../../utils/document_generation', () => ({ + generateSampleDocument: jest.fn(), +})); + +jest.mock('../../hooks/use_elasticsearch_url', () => ({ + useElasticsearchUrl: jest.fn(), +})); + +jest.mock('@kbn/search-api-keys-components', () => ({ + useSearchApiKey: jest.fn().mockReturnValue({ apiKey: 'test-api-key' }), +})); + +jest.mock('../../hooks/use_kibana', () => ({ + useKibana: jest.fn().mockReturnValue({ + services: { + application: {}, + share: {}, + console: {}, + }, + }), +})); + +jest.mock('../../contexts/usage_tracker_context', () => ({ + useUsageTracker: jest.fn().mockReturnValue({ + count: jest.fn(), + click: jest.fn(), + }), +})); + +describe('AddDocumentsCodeExample', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('generateSampleDocument', () => { + it('pass basic examples when mapping is default', () => { + const indexName = 'test-index'; + const mappingProperties: Record = { + vector: { type: 'dense_vector', dims: 3 }, + text: { type: 'text' }, + }; + + render( + + ); + + expect(generateSampleDocument).toHaveBeenCalledTimes(3); + + basicExampleTexts.forEach((text, index) => { + expect(generateSampleDocument).toHaveBeenNthCalledWith(index + 1, mappingProperties, text); + }); + }); + + it('pass basic examples when mapping is not passed', () => { + const indexName = 'test-index'; + + render(); + + expect(generateSampleDocument).toHaveBeenCalledTimes(3); + + const mappingProperties: Record = { + vector: { type: 'dense_vector', dims: 3 }, + text: { type: 'text' }, + }; + + basicExampleTexts.forEach((text, index) => { + expect(generateSampleDocument).toHaveBeenNthCalledWith(index + 1, mappingProperties, text); + }); + }); + + it('pass basic examples when mapping is default with extra vector fields', () => { + const indexName = 'test-index'; + const mappingProperties: Record = { + vector: { type: 'dense_vector', dims: 3, similarity: 'extra' }, + text: { type: 'text' }, + }; + + render( + + ); + + expect(generateSampleDocument).toHaveBeenCalledTimes(3); + + basicExampleTexts.forEach((text, index) => { + expect(generateSampleDocument).toHaveBeenNthCalledWith(index + 1, mappingProperties, text); + }); + }); + + it('pass examples text when mapping is custom', () => { + const indexName = 'test-index'; + const mappingProperties: Record = { + text: { type: 'text' }, + test: { type: 'boolean' }, + }; + + render( + + ); + + expect(generateSampleDocument).toHaveBeenCalledTimes(3); + + exampleTextsWithCustomMapping.forEach((text, index) => { + expect(generateSampleDocument).toHaveBeenNthCalledWith(index + 1, mappingProperties, text); + }); + }); + }); +}); diff --git a/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.tsx b/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.tsx index cdd773f4e6a81..b026a22fd16ce 100644 --- a/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.tsx +++ b/x-pack/plugins/search_indices/public/components/index_documents/add_documents_code_example.tsx @@ -6,10 +6,11 @@ */ import React, { useCallback, useMemo, useState } from 'react'; -import { MappingProperty } from '@elastic/elasticsearch/lib/api/types'; +import { MappingDenseVectorProperty, MappingProperty } from '@elastic/elasticsearch/lib/api/types'; import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TryInConsoleButton } from '@kbn/try-in-console'; +import { isEqual } from 'lodash'; import { useSearchApiKey } from '@kbn/search-api-keys-components'; import { useKibana } from '../../hooks/use_kibana'; @@ -24,6 +25,13 @@ import { CodeSample } from '../shared/code_sample'; import { generateSampleDocument } from '../../utils/document_generation'; import { getDefaultCodingLanguage } from '../../utils/language'; +export const basicExampleTexts = [ + 'Yellowstone National Park', + 'Yosemite National Park', + 'Rocky Mountain National Park', +]; +export const exampleTextsWithCustomMapping = [1, 2, 3].map((num) => `Example text ${num}`); + export interface AddDocumentsCodeExampleProps { indexName: string; mappingProperties: Record; @@ -56,10 +64,19 @@ export const AddDocumentsCodeExample = ({ [usageTracker] ); const sampleDocuments = useMemo(() => { - return [1, 2, 3].map((num) => - generateSampleDocument(codeSampleMappings, `Example text ${num}`) - ); - }, [codeSampleMappings]); + // If the default mapping was used, we need to exclude generated vector fields + const copyCodeSampleMappings = { + ...codeSampleMappings, + vector: { + type: codeSampleMappings.vector?.type, + dims: (codeSampleMappings.vector as MappingDenseVectorProperty)?.dims, + }, + }; + const isDefaultMapping = isEqual(copyCodeSampleMappings, ingestCodeExamples.defaultMapping); + const sampleTexts = isDefaultMapping ? basicExampleTexts : exampleTextsWithCustomMapping; + + return sampleTexts.map((text) => generateSampleDocument(codeSampleMappings, text)); + }, [codeSampleMappings, ingestCodeExamples.defaultMapping]); const { apiKey, apiKeyIsVisible } = useSearchApiKey(); const codeParams: IngestCodeSnippetParameters = useMemo(() => { return { diff --git a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts index df2632650f678..c9bd67aacdf2a 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_search_index_detail_page.ts @@ -180,6 +180,33 @@ export function SvlSearchIndexDetailPageProvider({ getService }: FtrProviderCont ); }, + async expectHasSampleDocuments() { + await testSubjects.existOrFail('ingestDataCodeExample-code-block'); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Yellowstone National Park' + ); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Yosemite National Park' + ); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Rocky Mountain National Park' + ); + }, + + async expectSampleDocumentsWithCustomMappings() { + await browser.refresh(); + await testSubjects.existOrFail('ingestDataCodeExample-code-block'); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Example text 1' + ); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Example text 2' + ); + expect(await testSubjects.getVisibleText('ingestDataCodeExample-code-block')).to.contain( + 'Example text 3' + ); + }, + async clickFirstDocumentDeleteAction() { await testSubjects.existOrFail('documentMetadataButton'); await testSubjects.click('documentMetadataButton');