[Enterprise Search] Redesign create index flow (#155149)

## Summary
This changes the create index flow in Enterprise Search Content to
frontload the configuration, and backload the actual index selection. It
also provides a more direct path from the integrations page, and
includes a few other goodies.



https://user-images.githubusercontent.com/94373878/232780443-80d926ce-cca3-4bef-b2ba-794c82c9f684.mov



https://user-images.githubusercontent.com/94373878/232780486-eadeeac0-e459-4c17-b6f0-71ba3a3c0017.mov




### Checklist

Delete any items that are not applicable to this PR.

- [x] 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)
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: Efe Gürkan YALAMAN <efeguerkan.yalaman@elastic.co>
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sander Philipse 2023-04-20 13:11:07 +02:00 committed by GitHub
parent 7c70508b7b
commit ac581be87d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1006 additions and 1139 deletions

View file

@ -129,8 +129,15 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
bulkApi: `${ELASTICSEARCH_DOCS}docs-bulk.html`,
configuration: `${ENTERPRISE_SEARCH_DOCS}configuration.html`,
connectors: `${ENTERPRISE_SEARCH_DOCS}connectors.html`,
connectorsAzureBlobStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-azure-blob.html`,
connectorsGoogleCloudStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-google-cloud.html`,
connectorsMongoDB: `${ENTERPRISE_SEARCH_DOCS}connectors-mongodb.html`,
connectorsMySQL: `${ENTERPRISE_SEARCH_DOCS}connectors-mysql.html`,
connectorsMicrosoftSQL: `${ENTERPRISE_SEARCH_DOCS}connectors-ms-sql.html`,
connectorsNetworkDrive: `${ENTERPRISE_SEARCH_DOCS}connectors-network-drive.html`,
connectorsOracle: `${ENTERPRISE_SEARCH_DOCS}connectors-oracle.html`,
connectorsPostgreSQL: `${ENTERPRISE_SEARCH_DOCS}connectors-postgresql.html`,
connectorsS3: `${ENTERPRISE_SEARCH_DOCS}connectors-s3.html`,
connectorsWorkplaceSearch: `${ENTERPRISE_SEARCH_DOCS}connectors.html#connectors-workplace-search`,
crawlerExtractionRules: `${ENTERPRISE_SEARCH_DOCS}crawler-extraction-rules.html`,
crawlerManaging: `${ENTERPRISE_SEARCH_DOCS}crawler-managing.html`,
@ -139,6 +146,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
documentLevelSecurity: `${ELASTICSEARCH_DOCS}document-level-security.html`,
elser: `${MACHINE_LEARNING_DOCS}ml-nlp-elser.html`,
engines: `${ENTERPRISE_SEARCH_DOCS}engines.html`,
ingestionApis: `${ENTERPRISE_SEARCH_DOCS}ingestion-apis.html`,
ingestPipelines: `${ENTERPRISE_SEARCH_DOCS}ingest-pipelines.html`,
languageAnalyzers: `${ELASTICSEARCH_DOCS}analysis-lang-analyzer.html`,
languageClients: `${ENTERPRISE_SEARCH_DOCS}programming-language-clients.html`,

View file

@ -114,8 +114,15 @@ export interface DocLinks {
readonly bulkApi: string;
readonly configuration: string;
readonly connectors: string;
readonly connectorsAzureBlobStorage: string;
readonly connectorsGoogleCloudStorage: string;
readonly connectorsMicrosoftSQL: string;
readonly connectorsMongoDB: string;
readonly connectorsMySQL: string;
readonly connectorsNetworkDrive: string;
readonly connectorsOracle: string;
readonly connectorsPostgreSQL: string;
readonly connectorsS3: string;
readonly connectorsWorkplaceSearch: string;
readonly crawlerExtractionRules: string;
readonly crawlerManaging: string;
@ -124,6 +131,7 @@ export interface DocLinks {
readonly documentLevelSecurity: string;
readonly elser: string;
readonly engines: string;
readonly ingestionApis: string;
readonly ingestPipelines: string;
readonly languageAnalyzers: string;
readonly languageClients: string;

View file

@ -157,10 +157,9 @@ export const DEFAULT_PIPELINE_VALUES: IngestPipelineParams = {
};
export enum INGESTION_METHOD_IDS {
api = 'api',
connector = 'connector',
crawler = 'crawler',
native_connector = 'native_connector',
API = 'api',
CONNECTOR = 'connector',
CRAWLER = 'crawler',
}
export const DEFAULT_PRODUCT_FEATURES: ProductFeatures = {

View file

@ -15,9 +15,9 @@ export const websiteSearchGuideId = 'websiteSearch';
export const databaseSearchGuideId = 'databaseSearch';
const apiMethods = {
[appSearchGuideId]: INGESTION_METHOD_IDS.api,
[databaseSearchGuideId]: INGESTION_METHOD_IDS.native_connector,
[websiteSearchGuideId]: INGESTION_METHOD_IDS.crawler,
[appSearchGuideId]: INGESTION_METHOD_IDS.API,
[databaseSearchGuideId]: INGESTION_METHOD_IDS.CONNECTOR,
[websiteSearchGuideId]: INGESTION_METHOD_IDS.CRAWLER,
};
export type EnterpriseSearchGuideIds =

View file

@ -20,7 +20,11 @@ describe('addConnectorApiLogic', () => {
it('calls correct api', async () => {
const promise = Promise.resolve({ id: 'unique id', index_name: 'indexName' });
http.post.mockReturnValue(promise);
const result = addConnector({ indexName: 'indexName', isNative: false, language: 'en' });
const result = addConnector({
indexName: 'indexName',
isNative: false,
language: 'en',
});
await nextTick();
expect(http.post).toHaveBeenCalledWith('/internal/enterprise_search/connectors', {
body: JSON.stringify({ index_name: 'indexName', is_native: false, language: 'en' }),

View file

@ -18,6 +18,7 @@ export interface AddConnectorApiLogicArgs {
indexName: string;
isNative: boolean;
language: string | null;
serviceType?: string;
}
export interface AddConnectorApiLogicResponse {
@ -30,6 +31,7 @@ export const addConnector = async ({
indexName,
isNative,
language,
serviceType,
}: AddConnectorApiLogicArgs): Promise<AddConnectorApiLogicResponse> => {
const route = '/internal/enterprise_search/connectors';
@ -41,6 +43,7 @@ export const addConnector = async ({
index_name: indexName,
is_native: isNative,
language,
service_type: serviceType,
};
const result = await HttpLogic.values.http.post<AddConnectorValue>(route, {
body: JSON.stringify(params),

View file

@ -1,36 +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 { createApiLogic } from '../../../shared/api_logic/create_api_logic';
import { HttpLogic } from '../../../shared/http';
import { NativeConnector } from '../../components/search_index/connector/types';
export interface SetNativeConnectorArgs {
connectorId: string;
serviceType: string;
}
export interface SetNativeConnectorResponse {
connectorId: string;
nativeConnector: NativeConnector;
}
export const setNativeConnector = async ({ connectorId, serviceType }: SetNativeConnectorArgs) => {
await HttpLogic.values.http.put(
`/internal/enterprise_search/connectors/${connectorId}/configure_native`,
{
body: JSON.stringify({ service_type: serviceType }),
}
);
return { connectorId };
};
export const SetNativeConnectorLogic = createApiLogic(
['content', 'service_type_connector_api_logic'],
setNativeConnector
);

View file

@ -5,11 +5,13 @@
* 2.0.
*/
import { setMockValues } from '../../../../__mocks__/kea_logic';
import React from 'react';
import { shallow } from 'enzyme';
import { EuiSteps } from '@elastic/eui';
import { Status } from '../../../../../../common/types/api';
import { NewSearchIndexTemplate } from '../new_search_index_template';
@ -18,6 +20,7 @@ import { MethodApi } from './method_api';
describe('MethodApi', () => {
beforeEach(() => {
jest.clearAllMocks();
setMockValues({ status: Status.IDLE });
});
it('renders API ingestion method tab', () => {
@ -25,6 +28,5 @@ describe('MethodApi', () => {
const template = wrapper.find(NewSearchIndexTemplate);
expect(template.prop('type')).toEqual('api');
expect(template.find(EuiSteps)).toHaveLength(1);
});
});

View file

@ -7,59 +7,22 @@
import React from 'react';
import { useActions } from 'kea';
import { useActions, useValues } from 'kea';
import { EuiSteps, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { Status } from '../../../../../../common/types/api';
import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps';
import { NewSearchIndexTemplate } from '../new_search_index_template';
import { MethodApiLogic } from './method_api_logic';
export const MethodApi: React.FC = () => {
const { makeRequest } = useActions(MethodApiLogic);
const { status } = useValues(MethodApiLogic);
return (
<NewSearchIndexTemplate
title={
<FormattedMessage
id="xpack.enterpriseSearch.content.newIndex.methodApi.title"
defaultMessage="Index using the API"
/>
}
type="api"
buttonLoading={status === Status.LOADING}
onSubmit={(indexName, language) => makeRequest({ indexName, language })}
>
<EuiSteps
steps={[
CREATE_ELASTICSEARCH_INDEX_STEP,
{
children: (
<EuiText size="s">
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content',
{
defaultMessage:
'Generate an API key and view the documentation for posting documents to the Elasticsearch API endpoint. Language clients are available for streamlined integration.',
}
)}
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title',
{
defaultMessage: 'Configure ingestion settings',
}
),
titleSize: 'xs',
},
BUILD_SEARCH_EXPERIENCE_STEP,
]}
/>
</NewSearchIndexTemplate>
/>
);
};

View file

@ -24,9 +24,14 @@ type MethodApiActions = Pick<
'apiSuccess' | 'makeRequest'
>;
export const MethodApiLogic = kea<MakeLogicType<{}, MethodApiActions>>({
interface MethodApiValues {
status: typeof CreateApiIndexApiLogic.values['status'];
}
export const MethodApiLogic = kea<MakeLogicType<MethodApiValues, MethodApiActions>>({
connect: {
actions: [CreateApiIndexApiLogic, ['apiSuccess', 'makeRequest']],
values: [CreateApiIndexApiLogic, ['status']],
},
listeners: {
apiSuccess: ({ indexName }) => {

View file

@ -11,8 +11,6 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiSteps } from '@elastic/eui';
import { Status } from '../../../../../../common/types/api';
import { NewSearchIndexTemplate } from '../new_search_index_template';
@ -27,10 +25,9 @@ describe('MethodConnector', () => {
});
it('renders connector ingestion method tab', () => {
const wrapper = shallow(<MethodConnector isNative={false} />);
const wrapper = shallow(<MethodConnector serviceType="mongodb" />);
const template = wrapper.find(NewSearchIndexTemplate);
expect(template.prop('type')).toEqual('connector');
expect(template.find(EuiSteps)).toHaveLength(1);
});
});

View file

@ -16,14 +16,10 @@ import {
EuiFlexItem,
EuiLink,
EuiSpacer,
EuiSteps,
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { Status } from '../../../../../../common/types/api';
import { docLinks } from '../../../../shared/doc_links';
import { KibanaLogic } from '../../../../shared/kibana';
@ -31,11 +27,11 @@ import { LicensingLogic } from '../../../../shared/licensing';
import { AddConnectorApiLogic } from '../../../api/connector/add_connector_api_logic';
import { FetchCloudHealthApiLogic } from '../../../api/stats/fetch_cloud_health_api_logic';
import { NATIVE_CONNECTORS } from '../../search_index/connector/constants';
import {
LicensingCallout,
LICENSING_FEATURE,
} from '../../shared/licensing_callout/licensing_callout';
import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps';
import { NewSearchIndexLogic } from '../new_search_index_logic';
import { NewSearchIndexTemplate } from '../new_search_index_template';
@ -43,7 +39,11 @@ import { errorToText } from '../utils/error_to_text';
import { AddConnectorLogic } from './add_connector_logic';
export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) => {
interface MethodConnectorProps {
serviceType: string;
}
export const MethodConnector: React.FC<MethodConnectorProps> = ({ serviceType }) => {
const { apiReset, makeRequest } = useActions(AddConnectorApiLogic);
const { error, status } = useValues(AddConnectorApiLogic);
const { isModalVisible } = useValues(AddConnectorLogic);
@ -53,6 +53,10 @@ export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) =
const { hasPlatinumLicense } = useValues(LicensingLogic);
const { data: cloudHealthData } = useValues(FetchCloudHealthApiLogic);
const isNative =
Boolean(NATIVE_CONNECTORS.find((connector) => connector.serviceType === serviceType)) &&
isCloud;
const isGated = isNative && !isCloud && !hasPlatinumLicense;
const hasLowMemory =
isNative && isCloud && cloudHealthData && !cloudHealthData.has_min_connector_memory;
@ -108,145 +112,62 @@ export const MethodConnector: React.FC<{ isNative: boolean }> = ({ isNative }) =
docsUrl={docLinks.connectors}
disabled={isGated || hasLowMemory}
error={errorToText(error)}
title={
isNative
? i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title',
{
defaultMessage: 'Index using a connector',
}
)
: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title',
{
defaultMessage: 'Build a connector',
}
)
}
type="connector"
onNameChange={() => {
apiReset();
}}
onSubmit={(name, lang) => makeRequest({ indexName: name, isNative, language: lang })}
onSubmit={(name, lang) =>
makeRequest({ indexName: name, isNative, language: lang, serviceType })
}
buttonLoading={status === Status.LOADING}
>
<EuiSteps
steps={[
CREATE_ELASTICSEARCH_INDEX_STEP,
isNative
? {
children: (
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content"
defaultMessage="Choose from our catalog of native connectors to start extracting searchable content from supported data sources like MongoDB. If you need to customize a connectors behavior, you can always deploy the self-managed connector client version and register it via the {buildAConnectorLabel} workflow."
values={{
buildAConnectorLabel: (
<strong>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title',
{
defaultMessage: 'Build a connector',
}
)}
</strong>
),
}}
/>
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title',
{
defaultMessage: 'Configure a connector',
}
),
titleSize: 'xs',
}
: {
children: (
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content"
defaultMessage="Using our connector framework and connector client examples, youll be able to accelerate ingestion to the Elasticsearch {bulkApiDocLink} for any data source. After creating your index, you will be guided through the steps to access the connector framework and connect your first connector client."
values={{
bulkApiDocLink: (
<EuiLink href={docLinks.bulkApi} target="_blank" external>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink',
{ defaultMessage: 'Bulk API' }
)}
</EuiLink>
),
}}
/>
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title',
{
defaultMessage: 'Build and configure a connector',
}
),
titleSize: 'xs',
},
BUILD_SEARCH_EXPERIENCE_STEP,
]}
/>
{isModalVisible && (
<EuiConfirmModal
title={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title',
{
defaultMessage: 'Replace existing connector',
}
)}
onCancel={(event) => {
event?.preventDefault();
setIsModalVisible(false);
}}
onConfirm={(event) => {
event.preventDefault();
makeRequest({
deleteExistingConnector: true,
/>
{isModalVisible && (
<EuiConfirmModal
title={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title',
{
defaultMessage: 'Replace existing connector',
}
)}
onCancel={() => {
setIsModalVisible(false);
}}
onConfirm={() => {
makeRequest({
deleteExistingConnector: true,
indexName: fullIndexName,
isNative,
language,
serviceType,
});
}}
cancelButtonText={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label',
{
defaultMessage: 'Cancel',
}
)}
confirmButtonText={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label',
{
defaultMessage: 'Replace configuration',
}
)}
defaultFocusedButton="confirm"
>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description',
{
defaultMessage:
'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?',
values: {
indexName: fullIndexName,
isNative,
language,
});
}}
cancelButtonText={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label',
{
defaultMessage: 'Cancel',
}
)}
confirmButtonText={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label',
{
defaultMessage: 'Replace configuration',
}
)}
defaultFocusedButton="confirm"
>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description',
{
defaultMessage:
'A deleted index named {indexName} was originally tied to an existing connector configuration. Would you like to replace the existing connector configuration with a new one?',
values: {
indexName: fullIndexName,
},
}
)}
</EuiConfirmModal>
)}
</NewSearchIndexTemplate>
},
}
)}
</EuiConfirmModal>
)}
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -11,8 +11,6 @@ import React from 'react';
import { shallow } from 'enzyme';
import { EuiSteps } from '@elastic/eui';
import { Status } from '../../../../../../common/types/api';
import { NewSearchIndexTemplate } from '../new_search_index_template';
@ -35,6 +33,5 @@ describe('MethodCrawler', () => {
const template = wrapper.find(NewSearchIndexTemplate);
expect(template.prop('type')).toEqual('crawler');
expect(template.find(EuiSteps)).toHaveLength(1);
});
});

View file

@ -9,9 +9,7 @@ import React from 'react';
import { useValues, useActions } from 'kea';
import { EuiFlexGroup, EuiFlexItem, EuiSteps, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Status } from '../../../../../../common/types/api';
import { docLinks } from '../../../../shared/doc_links';
@ -22,7 +20,6 @@ import {
LicensingCallout,
LICENSING_FEATURE,
} from '../../shared/licensing_callout/licensing_callout';
import { CREATE_ELASTICSEARCH_INDEX_STEP, BUILD_SEARCH_EXPERIENCE_STEP } from '../method_steps';
import { NewSearchIndexTemplate } from '../new_search_index_template';
import { MethodCrawlerLogic } from './method_crawler_logic';
@ -46,48 +43,12 @@ export const MethodCrawler: React.FC = () => {
)}
<EuiFlexItem>
<NewSearchIndexTemplate
title={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title',
{
defaultMessage: 'Index using the web crawler',
}
)}
type="crawler"
onSubmit={(indexName, language) => makeRequest({ indexName, language })}
disabled={isGated}
buttonLoading={status === Status.LOADING}
docsUrl={docLinks.crawlerOverview}
>
<EuiSteps
steps={[
CREATE_ELASTICSEARCH_INDEX_STEP,
{
children: (
<EuiText size="s">
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content',
{
defaultMessage:
'Configure the domains youd like to crawl, and when ready trigger your first crawl. Let Enterprise Search do the rest.',
}
)}
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title',
{
defaultMessage: 'Configure ingestion settings',
}
),
titleSize: 'xs',
},
BUILD_SEARCH_EXPERIENCE_STEP,
]}
/>
</NewSearchIndexTemplate>
/>
</EuiFlexItem>
</EuiFlexGroup>
);

View file

@ -1,98 +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 React from 'react';
import { EuiLink, EuiText } from '@elastic/eui';
import { EuiContainedStepProps } from '@elastic/eui/src/components/steps/steps';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import {
APP_SEARCH_URL,
ENTERPRISE_SEARCH_ELASTICSEARCH_URL,
} from '../../../../../common/constants';
import { docLinks } from '../../../shared/doc_links';
import { EuiLinkTo } from '../../../shared/react_router_helpers';
export const CREATE_ELASTICSEARCH_INDEX_STEP: EuiContainedStepProps = {
children: (
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.content.newIndex.steps.createIndex.content"
defaultMessage="Provide a unique index name and optionally set a default {languageAnalyzerDocLink} for the index. This index will hold your data source content, and is optimized with default field mappings for relevant search experiences."
values={{
languageAnalyzerDocLink: (
<EuiLink href={docLinks.languageAnalyzers} target="_blank" external>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink',
{ defaultMessage: 'language analyzer' }
)}
</EuiLink>
),
}}
/>
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate('xpack.enterpriseSearch.content.newIndex.steps.createIndex.title', {
defaultMessage: 'Create an Elasticsearch index',
}),
titleSize: 'xs',
};
export const BUILD_SEARCH_EXPERIENCE_STEP: EuiContainedStepProps = {
children: (
<EuiText size="s">
<p>
<FormattedMessage
id="xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content"
defaultMessage="After building your connector, your content is ready. Build your first search experience with {elasticsearchLink}, or explore the search experience tools provided by {appSearchLink}. We recommend that you create a {searchEngineLink} for the best balance of flexible power and turnkey simplicity."
values={{
appSearchLink: (
<EuiLinkTo to={APP_SEARCH_URL} shouldNotCreateHref>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink',
{ defaultMessage: 'App Search' }
)}
</EuiLinkTo>
),
elasticsearchLink: (
<EuiLinkTo to={ENTERPRISE_SEARCH_ELASTICSEARCH_URL} shouldNotCreateHref>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink',
{ defaultMessage: 'Elasticsearch' }
)}
</EuiLinkTo>
),
searchEngineLink: (
<EuiLinkTo to={`${APP_SEARCH_URL}/engines/new`} shouldNotCreateHref>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink',
{ defaultMessage: 'search engine' }
)}
</EuiLinkTo>
),
}}
/>
</p>
</EuiText>
),
status: 'incomplete',
title: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title',
{
defaultMessage: 'Build a search experience',
}
),
titleSize: 'xs',
};

View file

@ -1,22 +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.
*/
.entSearchNewIndexButtonGroupButton {
border: $euiBorderThin;
&--selected {
border: 1px $euiColorPrimary solid;
}
.rightArrow {
border-radius: $euiBorderRadiusSmall;
background: transparentize($euiColorPrimary, .8);
color: $euiColorPrimaryText;
padding: $euiSizeXS;
width: $euiSizeL;
}
}

View file

@ -11,216 +11,121 @@ import { useLocation } from 'react-router-dom';
import { useValues } from 'kea';
import {
EuiBadge,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { INGESTION_METHOD_IDS } from '../../../../../common/constants';
import { ProductFeatures } from '../../../../../common/types';
import { BETA_LABEL } from '../../../shared/constants/labels';
import { CONTINUE_BUTTON_LABEL } from '../../../shared/constants';
import { generateEncodedPath } from '../../../shared/encode_path_params';
import { KibanaLogic } from '../../../shared/kibana/kibana_logic';
import { parseQueryParams } from '../../../shared/query_params';
import { EuiLinkTo } from '../../../shared/react_router_helpers';
import { NEW_INDEX_METHOD_PATH, NEW_INDEX_SELECT_CONNECTOR_PATH } from '../../routes';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { baseBreadcrumbs } from '../search_indices';
import { ButtonGroup, ButtonGroupOption } from './button_group';
import { SearchIndexEmptyState } from './empty_state';
import { MethodApi } from './method_api/method_api';
import { MethodConnector } from './method_connector/method_connector';
import { MethodCrawler } from './method_crawler/method_crawler';
import { NewIndexCard } from './new_index_card';
const betaBadge = (
<EuiBadge iconType="beaker">
<EuiText size="xs">{BETA_LABEL}</EuiText>
</EuiBadge>
);
const METHOD_BUTTON_GROUP_OPTIONS: Record<INGESTION_METHOD_IDS, ButtonGroupOption> = {
[INGESTION_METHOD_IDS.crawler]: {
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description',
{
defaultMessage: 'Discover, extract, index, and sync all of your website content',
}
),
footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer', {
defaultMessage: 'No development required',
}),
icon: 'globe',
id: INGESTION_METHOD_IDS.crawler,
label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label', {
defaultMessage: 'Use the web crawler',
}),
},
[INGESTION_METHOD_IDS.native_connector]: {
badge: betaBadge,
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description',
{
defaultMessage:
'Configure a connector to extract, index, and sync all of your content from supported data sources ',
}
),
footer: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer',
{
defaultMessage: 'No development required',
}
),
icon: 'visVega',
id: INGESTION_METHOD_IDS.native_connector,
label: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label',
{
defaultMessage: 'Use a connector',
}
),
},
[INGESTION_METHOD_IDS.api]: {
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description',
{
defaultMessage: 'Add documents programmatically by connecting with the API',
}
),
footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer', {
defaultMessage: 'Some development required',
}),
icon: 'visVega',
id: INGESTION_METHOD_IDS.api,
label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label', {
defaultMessage: 'Use the API',
}),
},
[INGESTION_METHOD_IDS.connector]: {
badge: betaBadge,
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description',
{
defaultMessage:
'Use the connector framework to quickly build connectors for custom data sources',
}
),
footer: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer', {
defaultMessage: 'Development required',
}),
icon: 'package',
id: INGESTION_METHOD_IDS.connector,
label: i18n.translate('xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label', {
defaultMessage: 'Build a connector',
}),
},
};
const getAvailableMethodOptions = (productFeatures: ProductFeatures): ButtonGroupOption[] => {
const getAvailableMethodOptions = (productFeatures: ProductFeatures): INGESTION_METHOD_IDS[] => {
return [
...(productFeatures.hasWebCrawler
? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.crawler]]
: []),
...(productFeatures.hasNativeConnectors
? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.native_connector]]
: []),
METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.api],
...(productFeatures.hasConnectors
? [METHOD_BUTTON_GROUP_OPTIONS[INGESTION_METHOD_IDS.connector]]
: []),
...(productFeatures.hasWebCrawler ? [INGESTION_METHOD_IDS.CRAWLER] : []),
INGESTION_METHOD_IDS.API,
...(productFeatures.hasConnectors ? [INGESTION_METHOD_IDS.CONNECTOR] : []),
];
};
export const NewIndex: React.FC = () => {
const { search } = useLocation();
const { method } = parseQueryParams(search);
const { capabilities, productFeatures } = useValues(KibanaLogic);
const { method: methodParam } = parseQueryParams(search);
const availableIngestionMethodOptions = getAvailableMethodOptions(productFeatures);
const initialSelectedMethod =
availableIngestionMethodOptions.find((option) => option.id === methodParam) ??
availableIngestionMethodOptions[0];
const [selectedMethod, setSelectedMethod] = useState<ButtonGroupOption>(initialSelectedMethod);
const [selectedMethod, setSelectedMethod] = useState<string>(
Array.isArray(method) ? method[0] : method ?? INGESTION_METHOD_IDS.CRAWLER
);
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[
...baseBreadcrumbs,
i18n.translate('xpack.enterpriseSearch.content.newIndex.breadcrumb', {
defaultMessage: 'New search index',
defaultMessage: 'New ingestion method',
}),
]}
pageViewTelemetry="New Index"
isLoading={false}
pageHeader={{
description: i18n.translate('xpack.enterpriseSearch.content.newIndex.pageDescription', {
defaultMessage:
'Create a search optimized Elasticsearch index by selecting an ingestion method',
}),
pageTitle: i18n.translate('xpack.enterpriseSearch.content.newIndex.pageTitle', {
defaultMessage: 'New search index',
defaultMessage: 'Select an ingestion method',
}),
}}
>
<EuiFlexGroup>
<EuiFlexItem grow={false} style={{ maxWidth: '24rem' }}>
<EuiPanel hasShadow={false} paddingSize="m" grow={false} color="subdued">
<EuiTitle size="s">
<h2>
{i18n.translate('xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title', {
defaultMessage: 'Select an ingestion method',
})}
</h2>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s">
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description',
{
defaultMessage:
'Create a search optimized Elasticsearch index by selecting an ingestion method for your use case.',
}
<EuiFlexGroup direction="column">
<>
<EuiFlexItem>
<EuiFlexGroup>
{availableIngestionMethodOptions.map((type) => (
<EuiFlexItem key={type}>
<NewIndexCard
type={type}
onSelect={() => {
setSelectedMethod(type);
}}
isSelected={selectedMethod === type}
/>
</EuiFlexItem>
))}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem>
{capabilities.navLinks.integrations && (
<>
<EuiLinkTo to="/app/integrations" shouldNotCreateHref>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.viewIntegrationsLink',
{
defaultMessage: 'View additional integrations',
}
)}
</EuiLinkTo>
</>
)}
</p>
</EuiText>
<EuiSpacer size="m" />
<ButtonGroup
options={availableIngestionMethodOptions}
selected={selectedMethod}
onChange={setSelectedMethod}
/>
{capabilities.navLinks.integrations && (
<>
<EuiSpacer size="xxl" />
<EuiLinkTo to="/app/integrations" shouldNotCreateHref>
{i18n.translate('xpack.enterpriseSearch.content.newIndex.viewIntegrationsLink', {
defaultMessage: 'View additional integrations',
})}
</EuiLinkTo>
</>
)}
</EuiPanel>
</EuiFlexItem>
<EuiFlexItem>
{selectedMethod ? (
<>
{selectedMethod.id === INGESTION_METHOD_IDS.crawler && <MethodCrawler />}
{selectedMethod.id === INGESTION_METHOD_IDS.api && <MethodApi />}
{selectedMethod.id === INGESTION_METHOD_IDS.connector && (
<MethodConnector isNative={false} />
)}
{selectedMethod.id === INGESTION_METHOD_IDS.native_connector && (
<MethodConnector isNative />
)}
</>
) : (
<SearchIndexEmptyState />
)}
</EuiFlexItem>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
color="primary"
disabled={
!Object.values(INGESTION_METHOD_IDS as Record<string, string>).includes(
selectedMethod
)
}
fill
onClick={() => {
if (selectedMethod === INGESTION_METHOD_IDS.CONNECTOR) {
KibanaLogic.values.navigateToUrl(NEW_INDEX_SELECT_CONNECTOR_PATH);
} else {
KibanaLogic.values.navigateToUrl(
generateEncodedPath(NEW_INDEX_METHOD_PATH, { type: selectedMethod })
);
}
}}
>
{CONTINUE_BUTTON_LABEL}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</>
</EuiFlexGroup>
</EnterpriseSearchContentPageTemplate>
);

View file

@ -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, { MouseEventHandler } from 'react';
import { EuiCardProps, EuiIconProps, EuiTextColor } from '@elastic/eui';
import { EuiBadge, EuiButton, EuiCard, EuiIcon, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { INGESTION_METHOD_IDS } from '../../../../../common/constants';
import { getIngestionMethodIconType } from './utils';
export interface NewIndexCardProps {
isSelected?: boolean;
onSelect?: MouseEventHandler<HTMLButtonElement>;
type: INGESTION_METHOD_IDS;
}
export interface MethodCardOptions {
description: EuiCardProps['description'];
footer: Record<string, string>;
icon: EuiIconProps['type'];
title: EuiCardProps['title'];
}
const NO_DEVELOPMENT_LABEL = i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.noDevelopment.label',
{
defaultMessage: 'No development required',
}
);
const METHOD_CARD_OPTIONS: Record<INGESTION_METHOD_IDS, MethodCardOptions> = {
[INGESTION_METHOD_IDS.CRAWLER]: {
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.crawler.description',
{
defaultMessage: 'Discover, extract, index, and sync all of your website content',
}
),
footer: {
buttonLabel: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.crawler.label',
{
defaultMessage: 'Use a web crawler',
}
),
label: NO_DEVELOPMENT_LABEL,
},
icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.CRAWLER),
title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.crawler.title', {
defaultMessage: 'Web crawler',
}),
},
[INGESTION_METHOD_IDS.CONNECTOR]: {
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.connector.description',
{
defaultMessage:
'Use the connector framework to quickly build connectors for custom data sources',
}
),
footer: {
buttonLabel: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.connector.label',
{
defaultMessage: 'Use a connector',
}
),
label: NO_DEVELOPMENT_LABEL,
},
icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.CONNECTOR),
title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.connector.title', {
defaultMessage: 'Connector',
}),
},
[INGESTION_METHOD_IDS.API]: {
description: i18n.translate(
'xpack.enterpriseSearch.content.newIndex.methodCard.api.description',
{
defaultMessage: 'Add documents programmatically by connecting with the API',
}
),
footer: {
buttonLabel: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.label', {
defaultMessage: 'Use the API',
}),
label: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.footer', {
defaultMessage: 'Some development required',
}),
},
icon: getIngestionMethodIconType(INGESTION_METHOD_IDS.API),
title: i18n.translate('xpack.enterpriseSearch.content.newIndex.methodCard.api.title', {
defaultMessage: 'API',
}),
},
};
export const NewIndexCard: React.FC<NewIndexCardProps> = ({ onSelect, isSelected, type }) => {
if (!METHOD_CARD_OPTIONS[type]) {
return null;
}
const { icon, title, description, footer } = METHOD_CARD_OPTIONS[type];
return (
<EuiCard
hasBorder
icon={<EuiIcon type={icon} size="xxl" />}
title={title}
description={<EuiTextColor color="subdued">{description}</EuiTextColor>}
footer={
<>
<EuiBadge color="hollow">{footer.label}</EuiBadge>
<EuiSpacer size="m" />
<EuiButton
fullWidth
onClick={onSelect}
color={isSelected ? 'success' : 'primary'}
iconType={isSelected ? 'checkInCircleFilled' : undefined}
>
{footer.buttonLabel}
</EuiButton>
</>
}
/>
);
};

View file

@ -0,0 +1,37 @@
/*
* 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 { Switch } from 'react-router-dom';
import { Route } from '@kbn/shared-ux-router';
import {
NEW_INDEX_PATH,
NEW_INDEX_SELECT_CONNECTOR_PATH,
NEW_INDEX_METHOD_PATH,
} from '../../routes';
import { NewIndex } from './new_index';
import { NewSearchIndexPage } from './new_search_index_page';
import { SelectConnector } from './select_connector/select_connector';
export const NewIndexRouter: React.FC = () => {
return (
<Switch>
<Route path={NEW_INDEX_PATH} exact>
<NewIndex />
</Route>
<Route path={NEW_INDEX_SELECT_CONNECTOR_PATH} exact>
<SelectConnector />
</Route>
<Route path={NEW_INDEX_METHOD_PATH} exact>
<NewSearchIndexPage />
</Route>
</Switch>
);
};

View file

@ -0,0 +1,126 @@
/*
* 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 { useLocation, useParams } from 'react-router-dom';
import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { INGESTION_METHOD_IDS } from '../../../../../common/constants';
import { parseQueryParams } from '../../../shared/query_params';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { CONNECTORS } from '../search_index/connector/constants';
import { baseBreadcrumbs } from '../search_indices';
import { MethodApi } from './method_api/method_api';
import { MethodConnector } from './method_connector/method_connector';
import { MethodCrawler } from './method_crawler/method_crawler';
import { getIngestionMethodIconType } from './utils';
function getTitle(method: string, serviceType: string): string {
switch (method) {
case INGESTION_METHOD_IDS.API:
return i18n.translate('xpack.enterpriseSearch.content.new_index.apiTitle', {
defaultMessage: 'New search index',
});
case INGESTION_METHOD_IDS.CONNECTOR: {
const connector =
Boolean(serviceType) && CONNECTORS.find((item) => item.serviceType === serviceType);
return connector
? i18n.translate('xpack.enterpriseSearch.content.new_index.connectorTitleWithServiceType', {
defaultMessage: 'New {name} search index',
values: {
name: connector.name,
},
})
: i18n.translate('xpack.enterpriseSearch.content.new_index.connectorTitle', {
defaultMessage: 'New connector search index',
});
}
case INGESTION_METHOD_IDS.CRAWLER:
return i18n.translate('xpack.enterpriseSearch.content.new_index.crawlerTitle', {
defaultMessage: 'New web crawler search index',
});
default:
return i18n.translate('xpack.enterpriseSearch.content.new_index.genericTitle', {
defaultMessage: 'New search index',
});
}
}
function getDescription(method: string, serviceType: string): string {
switch (method) {
case INGESTION_METHOD_IDS.API:
return i18n.translate('xpack.enterpriseSearch.content.new_index.apiDescription', {
defaultMessage: 'A search index stores your data.',
});
case INGESTION_METHOD_IDS.CONNECTOR: {
const connector =
Boolean(serviceType) && CONNECTORS.find((item) => item.serviceType === serviceType);
return connector
? i18n.translate(
'xpack.enterpriseSearch.content.new_index.connectorDescriptionWithServiceType',
{
defaultMessage: 'A search index stores the data for your {name} connector.',
values: {
name: connector.name,
},
}
)
: i18n.translate('xpack.enterpriseSearch.content.new_index.connectorDescription', {
defaultMessage: 'A search index stores the data for your connector.',
});
}
case INGESTION_METHOD_IDS.CRAWLER:
return i18n.translate('xpack.enterpriseSearch.content.new_index.crawlerDescription', {
defaultMessage: 'A search index stores the data for your web crawler.',
});
default:
return i18n.translate('xpack.enterpriseSearch.content.new_index.defaultDescription', {
defaultMessage: 'A search index stores your data.',
});
}
}
export const NewSearchIndexPage: React.FC = () => {
const type = decodeURIComponent(useParams<{ type: string }>().type);
const { search } = useLocation();
const { service_type: inputServiceType } = parseQueryParams(search);
const serviceType = Array.isArray(inputServiceType)
? inputServiceType[0]
: inputServiceType || '';
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[...baseBreadcrumbs, 'New search index']}
pageViewTelemetry="New Index"
isLoading={false}
pageHeader={{
description: getDescription(type, serviceType),
pageTitle: (
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiIcon type={getIngestionMethodIconType(type)} size="xxl" />
</EuiFlexItem>
<EuiFlexItem>{getTitle(type, serviceType)}</EuiFlexItem>
</EuiFlexGroup>
),
}}
>
{
<>
{type === INGESTION_METHOD_IDS.CRAWLER && <MethodCrawler />}
{type === INGESTION_METHOD_IDS.API && <MethodApi />}
{type === INGESTION_METHOD_IDS.CONNECTOR && <MethodConnector serviceType={serviceType} />}
</>
}
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -20,7 +20,6 @@ import {
describe('NewSearchIndexTemplate', () => {
const mockProps: NewSearchIndexTemplateProps = {
onSubmit: jest.fn(),
title: 'Index using the API',
type: 'api',
};
@ -35,13 +34,9 @@ describe('NewSearchIndexTemplate', () => {
setMockActions({ makeRequest: jest.fn(), setLanguageSelectValue: jest.fn() });
});
it('renders children', () => {
const wrapper = shallow(
<NewSearchIndexTemplate {...mockProps}>
<div data-test-subj="ChildComponent" />
</NewSearchIndexTemplate>
);
it('renders', () => {
const wrapper = shallow(<NewSearchIndexTemplate {...mockProps} />);
expect(wrapper.find('[data-test-subj="ChildComponent"]')).toHaveLength(1);
expect(wrapper.find('EuiForm')).toHaveLength(1);
});
});

View file

@ -16,16 +16,19 @@ import {
EuiFlexItem,
EuiForm,
EuiFormRow,
EuiHorizontalRule,
EuiLink,
EuiPanel,
EuiSelect,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { INGESTION_METHOD_IDS } from '../../../../../common/constants';
import { BACK_BUTTON_LABEL } from '../../../shared/constants';
import { docLinks } from '../../../shared/doc_links';
import { SUPPORTED_LANGUAGES } from './constants';
import { NewSearchIndexLogic } from './new_search_index_logic';
import { LanguageForOptimization } from './types';
@ -37,19 +40,15 @@ export interface Props {
error?: string | React.ReactNode;
onNameChange?(name: string): void;
onSubmit(name: string, language: LanguageForOptimization): void;
title: React.ReactNode;
type: string;
}
export const NewSearchIndexTemplate: React.FC<Props> = ({
buttonLoading,
children,
disabled,
docsUrl,
error,
onNameChange,
onSubmit,
title,
type,
}) => {
const {
@ -102,26 +101,21 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
};
return (
<EuiPanel hasBorder>
<>
<EuiForm
component="form"
id="enterprise-search-add-connector"
id="enterprise-search-create-index"
onSubmit={(event) => {
event.preventDefault();
onSubmit(fullIndexName, language);
}}
>
<EuiFlexGroup direction="column">
<EuiFlexItem grow={false}>
<EuiTitle size="s">
<h2>{title}</h2>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow>
<EuiFlexGroup>
<EuiFlexItem grow>
<EuiFormRow
isDisabled={disabled}
isDisabled={disabled || buttonLoading}
label={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel',
{
@ -197,7 +191,66 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem>
<EuiLink target="_blank" href={docLinks.elasticsearchGettingStarted}>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreIndices.linkText',
{
defaultMessage: 'Learn more about indices',
}
)}
</EuiLink>
</EuiFlexItem>
{type === INGESTION_METHOD_IDS.CONNECTOR && (
<EuiFlexItem grow={false}>
<EuiLink target="_blank" href={docLinks.connectors}>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreConnectors.linkText',
{
defaultMessage: 'Learn more about connectors',
}
)}
</EuiLink>
</EuiFlexItem>
)}
{type === INGESTION_METHOD_IDS.CRAWLER && (
<EuiFlexItem grow={false}>
<EuiLink target="_blank" href={docLinks.crawlerOverview}>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreCrawler.linkText',
{
defaultMessage: 'Learn more about the Elastic web crawler',
}
)}
</EuiLink>
</EuiFlexItem>
)}
{type === INGESTION_METHOD_IDS.API && (
<EuiFlexItem grow={false}>
<EuiLink target="_blank" href={docLinks.ingestionApis}>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.learnMoreApis.linkText',
{
defaultMessage: 'Learn more about ingestion APIs',
}
)}
</EuiLink>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiSpacer />
<EuiFlexGroup direction="row" alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id={`entSearchContent-${type}-newIndex-goBack`}
isDisabled={buttonLoading}
onClick={() => history.back()}
>
{BACK_BUTTON_LABEL}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id={`entSearchContent-${type}-newIndex-createIndex`}
@ -214,22 +267,8 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
)}
</EuiButton>
</EuiFlexItem>
{!!docsUrl && (
<EuiFlexItem grow={false}>
<EuiLink target="_blank" href={docsUrl}>
{i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText',
{
defaultMessage: 'View the documentation',
}
)}
</EuiLink>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiForm>
<EuiHorizontalRule />
{children}
</EuiPanel>
</>
);
};

View file

@ -8,38 +8,40 @@
import React from 'react';
import {
EuiBadge,
EuiCheckableCard,
EuiCheckableCardProps,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { NATIVE_CONNECTOR_ICONS } from '../../../../../../assets/source_icons/native_connector_icons';
import './connector_checkable.scss';
import { BETA_LABEL } from '../../../../shared/constants';
export type ConnectorCheckableProps = Omit<
EuiCheckableCardProps,
'id' | 'label' | 'name' | 'value'
> & {
documentationUrl: string;
documentationUrl: string | undefined;
icon: string;
isBeta: boolean;
name: string;
serviceType: string;
};
export const ConnectorCheckable: React.FC<ConnectorCheckableProps> = ({
documentationUrl,
icon,
isBeta,
name,
serviceType,
...props
}) => {
const icon = NATIVE_CONNECTOR_ICONS[serviceType];
return (
<EuiCheckableCard
{...props}
@ -55,7 +57,7 @@ export const ConnectorCheckable: React.FC<ConnectorCheckableProps> = ({
)}
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<span>{name}</span>
<h2>{name}</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
@ -63,17 +65,7 @@ export const ConnectorCheckable: React.FC<ConnectorCheckableProps> = ({
name={name}
value={serviceType}
>
<div className="connectorCheckableContent">
<EuiText size="s" color="subdued">
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description',
{
defaultMessage: 'Search over your {name} content with Enterprise Search.',
values: { name },
}
)}
</EuiText>
<EuiSpacer size="s" />
{documentationUrl && (
<EuiLink target="_blank" href={documentationUrl}>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel',
@ -82,7 +74,12 @@ export const ConnectorCheckable: React.FC<ConnectorCheckableProps> = ({
}
)}
</EuiLink>
</div>
)}
{isBeta && (
<EuiBadge color="hollow" iconType="beaker">
<EuiText size="xs">{BETA_LABEL}</EuiText>
</EuiBadge>
)}
</EuiCheckableCard>
);
};

View file

@ -0,0 +1,135 @@
/*
* 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, { useState } from 'react';
import { useLocation } from 'react-router-dom';
import {
EuiButton,
EuiFlexGrid,
EuiFlexGroup,
EuiFlexItem,
EuiForm,
EuiFormFieldset,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { INGESTION_METHOD_IDS } from '../../../../../../common/constants';
import { BACK_BUTTON_LABEL, CONTINUE_BUTTON_LABEL } from '../../../../shared/constants';
import { generateEncodedPath } from '../../../../shared/encode_path_params';
import { KibanaLogic } from '../../../../shared/kibana';
import { parseQueryParams } from '../../../../shared/query_params';
import { NEW_INDEX_METHOD_PATH, NEW_INDEX_PATH } from '../../../routes';
import { EnterpriseSearchContentPageTemplate } from '../../layout';
import { CONNECTORS } from '../../search_index/connector/constants';
import { baseBreadcrumbs } from '../../search_indices';
import { ConnectorCheckable } from './connector_checkable';
export const SelectConnector: React.FC = () => {
const { search } = useLocation();
const { service_type: serviceType } = parseQueryParams(search);
const [selectedConnector, setSelectedConnector] = useState<string | null>(
Array.isArray(serviceType) ? serviceType[0] : serviceType ?? null
);
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[...baseBreadcrumbs, 'Select connector']}
pageViewTelemetry="select_connector"
isLoading={false}
pageHeader={{
description: i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.description',
{
defaultMessage: 'A search connector syncs data from a third party source.',
}
),
pageTitle: i18n.translate('xpack.enterpriseSearch.content.indices.selectConnector.title', {
defaultMessage: 'Select a connector',
}),
}}
>
<EuiForm
component="form"
onSubmit={(event) => {
event.preventDefault();
KibanaLogic.values.navigateToUrl(
`${generateEncodedPath(NEW_INDEX_METHOD_PATH, {
type: INGESTION_METHOD_IDS.CONNECTOR,
})}?service_type=${selectedConnector}`
);
}}
>
<EuiFormFieldset
legend={{
children: (
<EuiText color="subdued" size="s">
<p />
</EuiText>
),
}}
>
<EuiSpacer size="s" />
<EuiFlexGrid columns={3}>
{CONNECTORS.map((connector) => (
<EuiFlexItem key={connector.serviceType}>
<ConnectorCheckable
icon={connector.icon}
isBeta={connector.isBeta}
name={connector.name}
serviceType={connector.serviceType}
onChange={() => {
setSelectedConnector(connector.serviceType);
}}
documentationUrl={connector.docsUrl}
checked={selectedConnector === connector.serviceType}
/>
</EuiFlexItem>
))}
</EuiFlexGrid>
<EuiSpacer />
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<span>
<EuiButton
data-telemetry-id="entSearchContent-connector-selectConnector-backButton"
color="primary"
onClick={() => KibanaLogic.values.navigateToUrl(NEW_INDEX_PATH)}
>
{BACK_BUTTON_LABEL}
</EuiButton>
</span>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<span>
<EuiButton
data-telemetry-id="entSearchContent-connector-selectConnector-selectAndConfigure"
disabled={!selectedConnector}
fill
color="primary"
type="submit"
>
{CONTINUE_BUTTON_LABEL}
</EuiButton>
</span>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFormFieldset>
</EuiForm>
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -5,6 +5,12 @@
* 2.0.
*/
import { INGESTION_METHOD_IDS } from '../../../../../common/constants';
import connectorLogo from '../../../../assets/enterprise_search_features/connector.svg';
import crawlerLogo from '../../../../assets/enterprise_search_features/crawler.svg';
import { UNIVERSAL_LANGUAGE_VALUE } from './constants';
import { LanguageForOptimization } from './types';
@ -12,3 +18,14 @@ import { LanguageForOptimization } from './types';
// but we can't use null as the value for an EuiSelectOption
export const getLanguageForOptimization = (language: string): LanguageForOptimization =>
language === UNIVERSAL_LANGUAGE_VALUE ? null : language;
export function getIngestionMethodIconType(type: string): string {
switch (type) {
case INGESTION_METHOD_IDS.CRAWLER:
return crawlerLogo;
case INGESTION_METHOD_IDS.CONNECTOR:
return connectorLogo;
default:
return 'consoleApp';
}
}

View file

@ -43,6 +43,7 @@ import { SearchIndexTabId } from '../search_index';
import { ApiKeyConfig } from './api_key_configuration';
import { ConnectorConfigurationConfig } from './connector_configuration_config';
import { ConnectorNameAndDescription } from './connector_name_and_description/connector_name_and_description';
import { CONNECTORS } from './constants';
import { NativeConnectorConfiguration } from './native_connector_configuration/native_connector_configuration';
export const ConnectorConfiguration: React.FC = () => {
@ -59,6 +60,9 @@ export const ConnectorConfiguration: React.FC = () => {
}
const hasApiKey = !!(index.connector.api_key_id ?? apiKeyData);
const docsUrl = CONNECTORS.find(
({ serviceType }) => serviceType === index.connector.service_type
)?.docsUrl;
return (
<>
@ -422,6 +426,31 @@ export const ConnectorConfiguration: React.FC = () => {
)}
</EuiLink>
</EuiFlexItem>
{docsUrl && (
<EuiFlexItem>
<EuiLink href={docsUrl} target="_blank">
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.support.dockerDeploy.label',
{
defaultMessage: 'Deploy with Docker',
}
)}
</EuiLink>
</EuiFlexItem>
)}
<EuiFlexItem>
<EuiLink
href="https://github.com/elastic/connectors-python/blob/main/docs/CONFIG.md#run-the-connector-service-for-a-custom-connector"
target="_blank"
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.support.deploy.label',
{
defaultMessage: 'Deploy without Docker',
}
)}
</EuiLink>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</EuiFlexItem>

View file

@ -7,15 +7,20 @@
import { i18n } from '@kbn/i18n';
import { CONNECTOR_ICONS } from '../../../../../assets/source_icons/native_connector_icons';
import { docLinks } from '../../../../shared/doc_links';
import { NativeConnector } from './types';
import { ConnectorDefinition } from './types';
export const NATIVE_CONNECTORS: NativeConnector[] = [
export const CONNECTORS: ConnectorDefinition[] = [
{
docsUrl: docLinks.connectorsMongoDB,
externalAuthDocsUrl: 'https://www.mongodb.com/docs/atlas/app-services/authentication/',
externalDocsUrl: 'https://www.mongodb.com/docs/',
icon: CONNECTOR_ICONS.mongodb,
isBeta: false,
isNative: true,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mongodb.name', {
defaultMessage: 'MongoDB',
}),
@ -24,9 +29,114 @@ export const NATIVE_CONNECTORS: NativeConnector[] = [
{
docsUrl: docLinks.connectorsMySQL,
externalDocsUrl: 'https://dev.mysql.com/doc/',
icon: CONNECTOR_ICONS.mysql,
isBeta: false,
isNative: true,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.mysql.name', {
defaultMessage: 'MySQL',
}),
serviceType: 'mysql',
},
{
docsUrl: docLinks.connectorsAzureBlobStorage,
externalAuthDocsUrl: 'https://learn.microsoft.com/azure/storage/common/authorize-data-access',
externalDocsUrl: 'https://learn.microsoft.com/azure/storage/blobs/',
icon: CONNECTOR_ICONS.azure_blob_storage,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.azureBlob.name', {
defaultMessage: 'Azure Blob Storage',
}),
serviceType: 'azure_blob_storage',
},
{
docsUrl: docLinks.connectorsGoogleCloudStorage,
externalAuthDocsUrl: 'https://cloud.google.com/storage/docs/authentication',
externalDocsUrl: 'https://cloud.google.com/storage/docs',
icon: CONNECTOR_ICONS.google_cloud_storage,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.googleCloud.name', {
defaultMessage: 'Google Cloud Storage',
}),
serviceType: 'google_cloud_storage',
},
{
docsUrl: docLinks.connectorsMicrosoftSQL,
externalAuthDocsUrl:
'https://learn.microsoft.com/sql/relational-databases/security/authentication-access/getting-started-with-database-engine-permissions',
externalDocsUrl: 'https://learn.microsoft.com/sql/',
icon: CONNECTOR_ICONS.microsoft_sql,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.microsoftSQL.name', {
defaultMessage: 'Microsoft SQL',
}),
serviceType: 'mssql',
},
{
docsUrl: docLinks.connectorsNetworkDrive,
externalAuthDocsUrl: '',
externalDocsUrl: '',
icon: CONNECTOR_ICONS.network_drive,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.networkDrive.name', {
defaultMessage: 'Network drive',
}),
serviceType: 'network_drive',
},
{
docsUrl: docLinks.connectorsOracle,
externalAuthDocsUrl:
'https://docs.oracle.com/en/database/oracle/oracle-database/19/dbseg/index.html',
externalDocsUrl: 'https://docs.oracle.com/database/oracle/oracle-database/',
icon: CONNECTOR_ICONS.oracle,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.oracle.name', {
defaultMessage: 'Oracle',
}),
serviceType: 'oracle',
},
{
docsUrl: docLinks.connectorsPostgreSQL,
externalAuthDocsUrl: 'https://www.postgresql.org/docs/15/auth-methods.html',
externalDocsUrl: 'https://www.postgresql.org/docs/',
icon: CONNECTOR_ICONS.postgresql,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.postgresql.name', {
defaultMessage: 'Postgresql',
}),
serviceType: 'postgresql',
},
{
docsUrl: docLinks.connectorsS3,
externalAuthDocsUrl: 'https://docs.aws.amazon.com/s3/index.html',
externalDocsUrl: '',
icon: CONNECTOR_ICONS.amazon_s3,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.s3.name', {
defaultMessage: 'S3',
}),
serviceType: 's3',
},
{
docsUrl: docLinks.connectors,
externalAuthDocsUrl: '',
externalDocsUrl: '',
icon: CONNECTOR_ICONS.custom,
isBeta: true,
isNative: false,
name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.customConnector.name', {
defaultMessage: 'Custom connector',
}),
serviceType: '',
},
];
export const CUSTOM_CONNECTORS = CONNECTORS.filter(({ isNative }) => !isNative);
export const NATIVE_CONNECTORS = CONNECTORS.filter(({ isNative }) => isNative);

View file

@ -23,7 +23,6 @@ import {
import { i18n } from '@kbn/i18n';
import { NATIVE_CONNECTOR_ICONS } from '../../../../../../assets/source_icons/native_connector_icons';
import { docLinks } from '../../../../../shared/doc_links';
import { hasConfiguredConfiguration } from '../../../../utils/has_configured_configuration';
@ -55,8 +54,8 @@ export const NativeConnectorConfiguration: React.FC = () => {
const hasConfigured = hasConfiguredConfiguration(index.connector.configuration);
const hasConfiguredAdvanced = index.connector.last_synced || index.connector.scheduling.enabled;
const hasResearched = hasDescription || hasConfigured || hasConfiguredAdvanced;
const icon = nativeConnector.icon;
const icon = NATIVE_CONNECTOR_ICONS[nativeConnector.serviceType];
return (
<>
<EuiSpacer />

View file

@ -16,10 +16,10 @@ import { ConnectorStatus } from '../../../../../../../common/types/connectors';
import { docLinks } from '../../../../../shared/doc_links';
import { ConnectorConfigurationConfig } from '../connector_configuration_config';
import { NativeConnector } from '../types';
import { ConnectorDefinition } from '../types';
interface NativeConnectorConfigurationConfigProps {
nativeConnector: NativeConnector;
nativeConnector: ConnectorDefinition;
status: ConnectorStatus;
}

View file

@ -11,10 +11,10 @@ import { EuiText, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic
import { i18n } from '@kbn/i18n';
import { NativeConnector } from '../types';
import { ConnectorDefinition } from '../types';
interface ResearchConfigurationProps {
nativeConnector: NativeConnector;
nativeConnector: ConnectorDefinition;
}
export const ResearchConfiguration: React.FC<ResearchConfigurationProps> = ({
nativeConnector,

View file

@ -1,9 +0,0 @@
.connectorCheckable {
.euiRadio {
margin-top: 4px;
}
.connectorCheckableContent {
margin-left: 20px;
}
}

View file

@ -1,187 +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 React, { useEffect } from 'react';
import { useActions, useValues } from 'kea';
import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiFormFieldset,
EuiLink,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { Status } from '../../../../../../../common/types/api';
import { docLinks } from '../../../../../shared/doc_links';
import { generateEncodedPath } from '../../../../../shared/encode_path_params';
import { flashSuccessToast } from '../../../../../shared/flash_messages';
import { KibanaLogic } from '../../../../../shared/kibana';
import { EuiLinkTo } from '../../../../../shared/react_router_helpers';
import { CachedFetchIndexApiLogic } from '../../../../api/index/cached_fetch_index_api_logic';
import { NEW_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../../../routes';
import { isConnectorIndex } from '../../../../utils/indices';
import { EnterpriseSearchContentPageTemplate } from '../../../layout';
import { baseBreadcrumbs } from '../../../search_indices';
import { IndexNameLogic } from '../../index_name_logic';
import { NATIVE_CONNECTORS } from '../constants';
import { ConnectorCheckable } from './connector_checkable';
import { SelectConnectorLogic } from './select_connector_logic';
export const SelectConnector: React.FC = () => {
const { indexData, status: indexApiStatus } = useValues(CachedFetchIndexApiLogic);
const { selectedNativeConnector } = useValues(SelectConnectorLogic);
const { saveNativeConnector, setSelectedConnector } = useActions(SelectConnectorLogic);
const { indexName } = useValues(IndexNameLogic);
useEffect(() => {
if (isConnectorIndex(indexData) && indexData.connector.service_type) {
flashSuccessToast(
i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.successToast.title',
{
defaultMessage: 'Your index will now use the {connectorName} native connector.',
values: {
connectorName: NATIVE_CONNECTORS.find(
(connector) => connector.serviceType === indexData.connector.service_type
)?.name,
},
}
)
);
KibanaLogic.values.navigateToUrl(
generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName,
tabId: 'configuration',
})
);
}
}, [indexData]);
return (
<EnterpriseSearchContentPageTemplate
pageChrome={[...baseBreadcrumbs, indexName]}
pageViewTelemetry="select_connector"
isLoading={
indexApiStatus === Status.IDLE ||
(typeof indexData === 'undefined' && indexApiStatus === Status.LOADING)
}
pageHeader={{
pageTitle: indexName,
}}
>
<form
onSubmit={(event) => {
event.preventDefault();
saveNativeConnector();
}}
>
<EuiFormFieldset
legend={{
children: (
<>
<EuiTitle size="s">
<span>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.title',
{
defaultMessage: 'Select a connector',
}
)}
</span>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText color="subdued" size="s">
<p>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.description',
{
defaultMessage:
"Get started by selecting the connector you'd like to configure to extract, index and sync data from your data source into your newly created search index.",
}
)}
</p>
</EuiText>
</>
),
}}
>
<EuiSpacer size="s" />
<EuiFlexGroup direction="row">
{NATIVE_CONNECTORS.map((nativeConnector) => (
<EuiFlexItem key={nativeConnector.name}>
<ConnectorCheckable
name={nativeConnector.name}
serviceType={nativeConnector.serviceType}
onChange={() => setSelectedConnector(nativeConnector)}
documentationUrl={nativeConnector.docsUrl}
checked={nativeConnector === selectedNativeConnector}
/>
</EuiFlexItem>
))}
</EuiFlexGroup>
<EuiSpacer />
<EuiButton
data-telemetry-id="entSearchContent-connector-selectConnector-selectAndConfigure"
fill
color="primary"
type="submit"
disabled={selectedNativeConnector === null}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel',
{
defaultMessage: 'Select and configure',
}
)}
</EuiButton>
<EuiSpacer />
<EuiText size="s">
<FormattedMessage
id="xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage"
defaultMessage="Looking for more connectors? {workplaceSearchLink} or {buildYourOwnConnectorLink}."
values={{
buildYourOwnConnectorLink: (
<EuiLinkTo to={`${generateEncodedPath(NEW_INDEX_PATH, {})}?method=connector`}>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel',
{
defaultMessage: 'build your own',
}
)}
</EuiLinkTo>
),
workplaceSearchLink: (
<EuiLink target="_blank" href={docLinks.connectorsWorkplaceSearch}>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel',
{
defaultMessage: 'View additional integrations in Workplace Search',
}
)}
</EuiLink>
),
}}
/>
</EuiText>
</EuiFormFieldset>
</form>
</EnterpriseSearchContentPageTemplate>
);
};

View file

@ -1,96 +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 { kea, MakeLogicType } from 'kea';
import { Actions } from '../../../../../shared/api_logic/create_api_logic';
import { generateEncodedPath } from '../../../../../shared/encode_path_params';
import { KibanaLogic } from '../../../../../shared/kibana';
import {
SetNativeConnectorArgs,
SetNativeConnectorLogic,
SetNativeConnectorResponse,
} from '../../../../api/connector/set_native_connector_api_logic';
import { CachedFetchIndexApiLogic } from '../../../../api/index/cached_fetch_index_api_logic';
import { FetchIndexApiResponse } from '../../../../api/index/fetch_index_api_logic';
import { SEARCH_INDEX_TAB_PATH } from '../../../../routes';
import { isConnectorIndex } from '../../../../utils/indices';
import { NATIVE_CONNECTORS } from '../constants';
import { NativeConnector } from '../types';
type SelectConnectorActions = Pick<
Actions<SetNativeConnectorArgs, SetNativeConnectorResponse>,
'apiSuccess' | 'makeRequest'
> & {
saveNativeConnector(): void;
setSelectedConnector(nativeConnector: NativeConnector): {
nativeConnector: NativeConnector;
};
};
interface SelectConnectorValues {
index: FetchIndexApiResponse;
selectedNativeConnector: NativeConnector | null;
}
export const SelectConnectorLogic = kea<
MakeLogicType<SelectConnectorValues, SelectConnectorActions>
>({
actions: {
saveNativeConnector: true,
setSelectedConnector: (nativeConnector) => ({ nativeConnector }),
},
connect: {
actions: [SetNativeConnectorLogic, ['apiError', 'apiSuccess', 'makeRequest']],
values: [CachedFetchIndexApiLogic, ['indexData as index']],
},
events: ({ actions, values }) => ({
afterMount: () => {
if (isConnectorIndex(values.index)) {
const serviceType = values.index.connector.service_type;
const nativeConnector = NATIVE_CONNECTORS.find(
(connector) => connector.serviceType === serviceType
);
if (nativeConnector) {
actions.setSelectedConnector(nativeConnector);
}
}
},
}),
listeners: ({ actions, values }) => ({
apiSuccess: () => {
CachedFetchIndexApiLogic.actions.makeRequest({ indexName: values.index.name });
},
saveNativeConnector: () => {
if (!isConnectorIndex(values.index) || values.selectedNativeConnector === null) {
KibanaLogic.values.navigateToUrl(
generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName: values.index.name,
tabId: 'configuration',
})
);
} else {
actions.makeRequest({
connectorId: values.index.connector.id,
serviceType: values.selectedNativeConnector.serviceType,
});
}
},
}),
path: ['enterprise_search', 'content', 'select_connector'],
reducers: () => ({
selectedNativeConnector: [
null,
{
setSelectedConnector: (_, { nativeConnector }) => nativeConnector,
},
],
}),
});

View file

@ -5,10 +5,13 @@
* 2.0.
*/
export interface NativeConnector {
docsUrl: string;
export interface ConnectorDefinition {
docsUrl?: string;
externalAuthDocsUrl?: string;
externalDocsUrl: string;
icon: string;
isBeta: boolean;
isNative: boolean;
name: string;
serviceType: string;
}

View file

@ -19,11 +19,7 @@ import { i18n } from '@kbn/i18n';
import { generateEncodedPath } from '../../../shared/encode_path_params';
import { KibanaLogic } from '../../../shared/kibana';
import {
SEARCH_INDEX_PATH,
SEARCH_INDEX_SELECT_CONNECTOR_PATH,
SEARCH_INDEX_TAB_PATH,
} from '../../routes';
import { SEARCH_INDEX_PATH, SEARCH_INDEX_TAB_PATH } from '../../routes';
import { isConnectorIndex, isCrawlerIndex } from '../../utils/indices';
import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
@ -99,19 +95,6 @@ export const SearchIndex: React.FC = () => {
}
}, [isAppGuideActive, isWebsiteGuideActive, isDatabaseGuideActive, index?.count]);
useEffect(() => {
if (
isConnectorIndex(index) &&
index.name === indexName &&
index.connector.is_native &&
index.connector.service_type === null
) {
KibanaLogic.values.navigateToUrl(
generateEncodedPath(SEARCH_INDEX_SELECT_CONNECTOR_PATH, { indexName })
);
}
}, [index]);
const ALL_INDICES_TABS: EuiTabbedContentTab[] = [
{
content: <SearchIndexOverview />,

View file

@ -15,12 +15,10 @@ import { Route } from '@kbn/shared-ux-router';
import {
OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH,
SEARCH_INDEX_PATH,
SEARCH_INDEX_SELECT_CONNECTOR_PATH,
SEARCH_INDEX_TAB_DETAIL_PATH,
SEARCH_INDEX_TAB_PATH,
} from '../../routes';
import { SelectConnector } from './connector/select_connector/select_connector';
import { IndexNameLogic } from './index_name_logic';
import { IndexViewLogic } from './index_view_logic';
import { SearchIndex } from './search_index';
@ -48,9 +46,6 @@ export const SearchIndexRouter: React.FC = () => {
<Route path={SEARCH_INDEX_PATH} exact>
<SearchIndex />
</Route>
<Route path={SEARCH_INDEX_SELECT_CONNECTOR_PATH} exact>
<SelectConnector />
</Route>
<Route path={SEARCH_INDEX_TAB_DETAIL_PATH}>
<SearchIndex />
</Route>

View file

@ -12,7 +12,7 @@ import { Switch } from 'react-router-dom';
import { shallow } from 'enzyme';
import { NewIndex } from '../new_index';
import { NewIndexRouter } from '../new_index/new_index_router';
import { SearchIndexRouter } from '../search_index/search_index_router';
import { SearchIndices } from './search_indices';
@ -25,7 +25,7 @@ describe('SearchIndicesRouter', () => {
const routeSwitch = wrapper.find(Switch);
expect(routeSwitch.find(NewIndex)).toHaveLength(1);
expect(routeSwitch.find(NewIndexRouter)).toHaveLength(1);
expect(routeSwitch.find(SearchIndices)).toHaveLength(1);
expect(routeSwitch.find(SearchIndexRouter)).toHaveLength(1);
});

View file

@ -12,7 +12,7 @@ import { Route } from '@kbn/shared-ux-router';
import { SEARCH_INDICES_PATH, SEARCH_INDEX_PATH, NEW_INDEX_PATH } from '../../routes';
import { NewIndex } from '../new_index';
import { NewIndexRouter } from '../new_index/new_index_router';
import { SearchIndexRouter } from '../search_index/search_index_router';
import { SearchIndices } from './search_indices';
@ -20,8 +20,8 @@ import { SearchIndices } from './search_indices';
export const SearchIndicesRouter: React.FC = () => {
return (
<Switch>
<Route exact path={NEW_INDEX_PATH}>
<NewIndex />
<Route path={NEW_INDEX_PATH}>
<NewIndexRouter />
</Route>
<Route exact path={SEARCH_INDICES_PATH}>
<SearchIndices />

View file

@ -13,16 +13,17 @@ export const SEARCH_INDICES_PATH = `${ROOT_PATH}search_indices`;
export const SETTINGS_PATH = `${ROOT_PATH}settings`;
export const NEW_INDEX_PATH = `${SEARCH_INDICES_PATH}/new_index`;
export const NEW_INDEX_METHOD_PATH = `${NEW_INDEX_PATH}/:type`;
export const NEW_API_PATH = `${NEW_INDEX_PATH}/api`;
export const NEW_ES_INDEX_PATH = `${NEW_INDEX_PATH}/elasticsearch`;
export const NEW_DIRECT_UPLOAD_PATH = `${NEW_INDEX_PATH}/upload`;
export const NEW_INDEX_SELECT_CONNECTOR_PATH = `${NEW_INDEX_PATH}/select_connector`;
export const SEARCH_INDEX_PATH = `${SEARCH_INDICES_PATH}/:indexName`;
export const SEARCH_INDEX_TAB_PATH = `${SEARCH_INDEX_PATH}/:tabId`;
export const SEARCH_INDEX_TAB_DETAIL_PATH = `${SEARCH_INDEX_TAB_PATH}/:detailId`;
export const SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/domain_management/:domainId`;
export const OLD_SEARCH_INDEX_CRAWLER_DOMAIN_DETAIL_PATH = `${SEARCH_INDEX_PATH}/crawler/domains/:domainId`;
export const SEARCH_INDEX_SELECT_CONNECTOR_PATH = `${SEARCH_INDEX_PATH}/select_connector`;
export const ENGINES_PATH = `${ROOT_PATH}engines`;

View file

@ -60,8 +60,15 @@ class DocLinks {
public clientsRustOverview: string;
public cloudIndexManagement: string;
public connectors: string;
public connectorsAzureBlobStorage: string;
public connectorsGoogleCloudStorage: string;
public connectorsMicrosoftSQL: string;
public connectorsMongoDB: string;
public connectorsMySQL: string;
public connectorsNetworkDrive: string;
public connectorsOracle: string;
public connectorsPostgreSQL: string;
public connectorsS3: string;
public connectorsWorkplaceSearch: string;
public crawlerExtractionRules: string;
public crawlerManaging: string;
@ -78,6 +85,7 @@ class DocLinks {
public enterpriseSearchMailService: string;
public enterpriseSearchTroubleshootSetup: string;
public enterpriseSearchUsersAccess: string;
public ingestionApis: string;
public ingestPipelines: string;
public kibanaSecurity: string;
public languageAnalyzers: string;
@ -179,8 +187,15 @@ class DocLinks {
this.clientsRustOverview = '';
this.cloudIndexManagement = '';
this.connectors = '';
this.connectorsAzureBlobStorage = '';
this.connectorsGoogleCloudStorage = '';
this.connectorsMicrosoftSQL = '';
this.connectorsMongoDB = '';
this.connectorsMySQL = '';
this.connectorsNetworkDrive = '';
this.connectorsOracle = '';
this.connectorsPostgreSQL = '';
this.connectorsS3 = '';
this.connectorsWorkplaceSearch = '';
this.crawlerExtractionRules = '';
this.crawlerManaging = '';
@ -197,6 +212,7 @@ class DocLinks {
this.enterpriseSearchMailService = '';
this.enterpriseSearchTroubleshootSetup = '';
this.enterpriseSearchUsersAccess = '';
this.ingestionApis = '';
this.ingestPipelines = '';
this.kibanaSecurity = '';
this.languageAnalyzers = '';
@ -317,6 +333,7 @@ class DocLinks {
this.enterpriseSearchMailService = docLinks.links.enterpriseSearch.mailService;
this.enterpriseSearchTroubleshootSetup = docLinks.links.enterpriseSearch.troubleshootSetup;
this.enterpriseSearchUsersAccess = docLinks.links.enterpriseSearch.usersAccess;
this.ingestionApis = docLinks.links.enterpriseSearch.ingestionApis;
this.ingestPipelines = docLinks.links.enterpriseSearch.ingestPipelines;
this.kibanaSecurity = docLinks.links.kibana.xpackSecurity;
this.languageAnalyzers = docLinks.links.enterpriseSearch.languageAnalyzers;

View file

@ -1 +1 @@
<svg fill="none" height="100" viewBox="0 0 100 100" width="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h100v100h-100z"/></clipPath><g clip-path="url(#a)"><path d="m50.8142 23.5717h-4.2c4.143 8.1938 6.3015 17.2469 6.3015 26.4286 0 9.1816-2.1585 18.2347-6.3015 26.4285h4.5714c7.0093 0 13.7316-2.7844 18.6879-7.7407s7.7407-11.6785 7.7407-18.6878-2.7844-13.7315-7.7407-18.6879c-4.9563-4.9563-11.6786-7.7407-18.6879-7.7407zm-.8142-23.5857279c-6.6174-.0042253-13.1691 1.3119779-19.2715 3.8714579 5.3516 4.32083 9.9142 9.53597 13.4857 15.41427h8.0714l.0715.0857c7.7784.5043 15.0733 3.9498 20.4037 9.637s8.2968 13.1897 8.2968 20.9844-2.9664 15.2973-8.2968 20.9845-12.6253 9.1327-20.4037 9.6369l-.0715.1001h-8.0714c-3.5715 5.8782-8.1341 11.0935-13.4857 15.4143 6.8282 2.8556 14.2051 4.1594 21.5985 3.8181 7.3933-.3415 14.6186-2.3201 21.1544-5.7929 6.5358-3.4729 12.2191-8.3534 16.6398-14.2894 4.4206-5.936 7.4683-12.7794 8.9231-20.0362 1.4547-7.2568 1.2807-14.7461-.5104-21.9274-1.791-7.1813-5.1539-13.8753-9.846-19.5991s-10.5964-10.33458-17.2868-13.49953c-6.6903-3.16494-13.9999-4.8051405-21.4011-4.8021979zm-10.8714 19.2857279c-3.4579-5.1394-7.7617-9.65573-12.7287-13.35712-7.308 3.93332-13.5268 9.61712-18.09986 16.54282 4.21966-2.0886 8.86316-3.1785 13.57146-3.1857zm0 61.4286h-17.2571c-4.7102-.005-9.3553-1.1002-13.57146-3.2 4.57606 6.9408 10.80566 12.6353 18.12846 16.5714 4.9508-3.7106 9.2437-8.2257 12.7001-13.3571zm2.6428-4.2858c4.4971-8.0826 6.8574-17.1791 6.8574-26.4285 0-9.2495-2.3603-18.346-6.8574-26.4286h-19.8999c-6.6584-.0024-13.07027 2.5194-17.94297 7.0571-.04357.1274-.09606.2515-.15712.3715 1.44454-1.374 3.03486-2.586 4.74278-3.6143-1.70792 1.0283-3.29824 2.2403-4.74278 3.6143-5.02806 12.1627-5.02806 25.823 0 37.9857.06106.12.11355.244.15712.3714 4.87578 4.5328 11.28557 7.0539 17.94297 7.0571z" fill="#0060d5"/></g></svg>
<svg fill="none" height="100" viewBox="0 0 100 100" width="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h100v100h-100z"/></clipPath><g clip-path="url(#a)"><path d="m50.8142 23.5717h-4.2c4.143 8.1938 6.3015 17.2469 6.3015 26.4286 0 9.1816-2.1585 18.2347-6.3015 26.4285h4.5714c7.0093 0 13.7316-2.7844 18.6879-7.7407s7.7407-11.6785 7.7407-18.6878-2.7844-13.7315-7.7407-18.6879c-4.9563-4.9563-11.6786-7.7407-18.6879-7.7407zm-.8142-23.5857279c-6.6174-.0042253-13.1691 1.3119779-19.2715 3.8714579 5.3516 4.32083 9.9142 9.53597 13.4857 15.41427h8.0714l.0715.0857c7.7784.5043 15.0733 3.9498 20.4037 9.637s8.2968 13.1897 8.2968 20.9844-2.9664 15.2973-8.2968 20.9845-12.6253 9.1327-20.4037 9.6369l-.0715.1001h-8.0714c-3.5715 5.8782-8.1341 11.0935-13.4857 15.4143 6.8282 2.8556 14.2051 4.1594 21.5985 3.8181 7.3933-.3415 14.6186-2.3201 21.1544-5.7929 6.5358-3.4729 12.2191-8.3534 16.6398-14.2894 4.4206-5.936 7.4683-12.7794 8.9231-20.0362 1.4547-7.2568 1.2807-14.7461-.5104-21.9274-1.791-7.1813-5.1539-13.8753-9.846-19.5991s-10.5964-10.33458-17.2868-13.49953c-6.6903-3.16494-13.9999-4.8051405-21.4011-4.8021979zm-10.8714 19.2857279c-3.4579-5.1394-7.7617-9.65573-12.7287-13.35712-7.308 3.93332-13.5268 9.61712-18.09986 16.54282 4.21966-2.0886 8.86316-3.1785 13.57146-3.1857zm0 61.4286h-17.2571c-4.7102-.005-9.3553-1.1002-13.57146-3.2 4.57606 6.9408 10.80566 12.6353 18.12846 16.5714 4.9508-3.7106 9.2437-8.2257 12.7001-13.3571zm2.6428-4.2858c4.4971-8.0826 6.8574-17.1791 6.8574-26.4285 0-9.2495-2.3603-18.346-6.8574-26.4286h-19.8999c-6.6584-.0024-13.07027 2.5194-17.94297 7.0571-.04357.1274-.09606.2515-.15712.3715 1.44454-1.374 3.03486-2.586 4.74278-3.6143-1.70792 1.0283-3.29824 2.2403-4.74278 3.6143-5.02806 12.1627-5.02806 25.823 0 37.9857.06106.12.11355.244.15712.3714 4.87578 4.5328 11.28557 7.0539 17.94297 7.0571z" fill="#0060d5"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

@ -0,0 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1173_3)">
<path d="M13.4118 25.6592L6.3408 18.5879L8.57147 17.9904L14.0095 23.4285L21.4381 21.438L23.4286 14.0094L17.9906 8.57145L18.5883 6.34074L25.6593 13.4117L23.37 21.9555L28.3199 26.9053L28.021 28.0206L26.9056 28.3195L21.9559 23.3698L13.4118 25.6592Z" fill="#535766"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.5353 19.5357L14.7057 20.8298L11.1702 17.2942L12.1654 13.5799L3.68021 5.09471L3.9791 3.97921L5.09432 3.68039L13.5797 12.1657L17.2939 11.1705L20.8294 14.7061L19.5353 19.5357ZM18.5987 15.3038L17.9023 17.9027L15.3034 18.5991L13.4009 16.6965L14.0973 14.0976L16.6962 13.4012L18.5987 15.3038Z" fill="#00BFB3"/>
</g>
<defs>
<clipPath id="clip0_1173_3">
<rect width="32" height="32" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 864 B

View file

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9999 2.57744V1.57734L15.9999 1L16.9999 1.57736V2.57726L27.1244 8.42263L27.9903 7.92268L28.9903 8.5V9.65477L28.1244 10.1547V21.8453L28.9903 22.3452V23.5L27.9904 24.0773L27.1244 23.5774L16.9999 29.4227V30.4226L15.9999 31L14.9999 30.4227V29.4226L4.87569 23.5773L4.00958 24.0774L3.00952 23.5V22.3454L3.87573 21.8453V10.1547L3.00952 9.65462V8.5L4.00959 7.92261L4.87571 8.42266L14.9999 2.57744ZM16.9999 10.5772L20.1962 12.4226L21.6603 11.5773L16.9999 8.88665V10.5772ZM21.1963 14.1546V17.8453L22.6604 18.6906V13.3094L21.1963 14.1546ZM20.1962 19.5774L16.9999 21.4228V23.1133L21.6603 20.4227L20.1962 19.5774ZM14.9999 21.4225L11.8039 19.5773L10.3398 20.4226L14.9999 23.1131V21.4225ZM10.804 17.8452V14.1547L9.33984 13.3094V18.6905L10.804 17.8452ZM11.8039 12.4227L14.9999 10.5775V8.88685L10.3398 11.5774L11.8039 12.4227ZM23.6603 10.4226L16.9999 6.57725V4.88666L25.1244 9.57733L23.6603 10.4226ZM24.6604 19.8453V12.1547L26.1244 11.3094V20.6906L24.6604 19.8453ZM16.9999 25.4228L23.6603 21.5774L25.1244 22.4227L16.9999 27.1133V25.4228ZM8.3398 21.5773L14.9999 25.4226V27.1132L6.87569 22.4226L8.3398 21.5773ZM7.33984 12.1547V19.8452L5.87573 20.6906V11.3094L7.33984 12.1547ZM14.9999 6.57745L8.33981 10.4227L6.87571 9.57736L14.9999 4.88684V6.57745ZM12.804 14.1547L16.0001 12.3094L19.1963 14.1547V17.8453L16.0001 19.6906L12.804 17.8453V14.1547Z" fill="#535766"/>
<path d="M16 14L17.7321 15V17L16 18L14.2679 17V15L16 14Z" fill="#00BFB3"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1 @@
<svg fill="none" height="100" viewBox="0 0 100 100" width="100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><clipPath id="a"><path d="m0 0h100v100h-100z"/></clipPath><g clip-path="url(#a)"><path d="m50.8142 23.5717h-4.2c4.143 8.1938 6.3015 17.2469 6.3015 26.4286 0 9.1816-2.1585 18.2347-6.3015 26.4285h4.5714c7.0093 0 13.7316-2.7844 18.6879-7.7407s7.7407-11.6785 7.7407-18.6878-2.7844-13.7315-7.7407-18.6879c-4.9563-4.9563-11.6786-7.7407-18.6879-7.7407zm-.8142-23.5857279c-6.6174-.0042253-13.1691 1.3119779-19.2715 3.8714579 5.3516 4.32083 9.9142 9.53597 13.4857 15.41427h8.0714l.0715.0857c7.7784.5043 15.0733 3.9498 20.4037 9.637s8.2968 13.1897 8.2968 20.9844-2.9664 15.2973-8.2968 20.9845-12.6253 9.1327-20.4037 9.6369l-.0715.1001h-8.0714c-3.5715 5.8782-8.1341 11.0935-13.4857 15.4143 6.8282 2.8556 14.2051 4.1594 21.5985 3.8181 7.3933-.3415 14.6186-2.3201 21.1544-5.7929 6.5358-3.4729 12.2191-8.3534 16.6398-14.2894 4.4206-5.936 7.4683-12.7794 8.9231-20.0362 1.4547-7.2568 1.2807-14.7461-.5104-21.9274-1.791-7.1813-5.1539-13.8753-9.846-19.5991s-10.5964-10.33458-17.2868-13.49953c-6.6903-3.16494-13.9999-4.8051405-21.4011-4.8021979zm-10.8714 19.2857279c-3.4579-5.1394-7.7617-9.65573-12.7287-13.35712-7.308 3.93332-13.5268 9.61712-18.09986 16.54282 4.21966-2.0886 8.86316-3.1785 13.57146-3.1857zm0 61.4286h-17.2571c-4.7102-.005-9.3553-1.1002-13.57146-3.2 4.57606 6.9408 10.80566 12.6353 18.12846 16.5714 4.9508-3.7106 9.2437-8.2257 12.7001-13.3571zm2.6428-4.2858c4.4971-8.0826 6.8574-17.1791 6.8574-26.4285 0-9.2495-2.3603-18.346-6.8574-26.4286h-19.8999c-6.6584-.0024-13.07027 2.5194-17.94297 7.0571-.04357.1274-.09606.2515-.15712.3715 1.44454-1.374 3.03486-2.586 4.74278-3.6143-1.70792 1.0283-3.29824 2.2403-4.74278 3.6143-5.02806 12.1627-5.02806 25.823 0 37.9857.06106.12.11355.244.15712.3714 4.87578 4.5328 11.28557 7.0539 17.94297 7.0571z" fill="#0060d5"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -5,10 +5,26 @@
* 2.0.
*/
import amazon_s3 from './amazon_s3.svg';
import azure_blob_storage from './azure_blob_storage.svg';
import custom from './custom.svg';
import google_cloud_storage from './google_cloud_storage.svg';
import microsoft_sql from './microsoft_sql.svg';
import mongodb from './mongodb.svg';
import mysql from './mysql.svg';
import network_drive from './network_drive.svg';
import oracle from './oracle.svg';
import postgresql from './postgresql.svg';
export const NATIVE_CONNECTOR_ICONS: Record<string, string | undefined> = {
export const CONNECTOR_ICONS = {
amazon_s3,
azure_blob_storage,
custom,
google_cloud_storage,
microsoft_sql,
mongodb,
mysql,
network_drive,
oracle,
postgresql,
};

View file

@ -372,7 +372,7 @@ export const registerEnterpriseSearchIntegrations = (
defaultMessage: 'Add search to your website with the Enterprise Search web crawler.',
}),
categories: ['enterprise_search', 'app_search', 'web', 'elastic_stack', 'crawler'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=crawler',
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/crawler',
icons: [
{
type: 'eui',
@ -393,7 +393,7 @@ export const registerEnterpriseSearchIntegrations = (
defaultMessage: "Add search to your application with Elasticsearch's robust APIs.",
}),
categories: ['enterprise_search', 'custom', 'elastic_stack', 'sdk_search', 'language_client'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=api',
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/api',
icons: [
{
type: 'eui',
@ -421,8 +421,7 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'custom', 'elastic_stack', 'connector', 'native_search'],
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index?method=native_connector',
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index/connector',
icons: [
{
type: 'eui',
@ -453,7 +452,7 @@ export const registerEnterpriseSearchIntegrations = (
'connector_client',
],
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index?method=native_connector',
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=mongodb',
icons: [
{
type: 'svg',
@ -484,7 +483,7 @@ export const registerEnterpriseSearchIntegrations = (
'connector_client',
],
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index?method=native_connector',
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=mysql',
icons: [
{
type: 'svg',
@ -509,7 +508,8 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'custom', 'elastic_stack', 'connector_client'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=custom',
icons: [
{
type: 'eui',
@ -532,7 +532,8 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'elastic_stack', 'custom', 'datastore'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=postgresql',
icons: [
{
type: 'svg',
@ -557,7 +558,8 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'elastic_stack', 'custom', 'datastore'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=oracle',
icons: [
{
type: 'svg',
@ -569,7 +571,7 @@ export const registerEnterpriseSearchIntegrations = (
});
customIntegrations.registerCustomIntegration({
id: 'ms_sql',
id: 'mssql',
title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.msSqlName', {
defaultMessage: 'Microsoft SQL',
}),
@ -581,7 +583,8 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'custom', 'elastic_stack', 'datastore'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=mssql',
icons: [
{
type: 'svg',
@ -617,7 +620,8 @@ export const registerEnterpriseSearchIntegrations = (
'connector_client',
'connector_package',
],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=network_drive',
icons: [
{
type: 'svg',
@ -642,7 +646,8 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'datastore', 'elastic_stack'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=s3',
icons: [
{
type: 'svg',
@ -666,12 +671,13 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'elastic_stack', 'custom'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=google_cloud_storage',
icons: [
{
type: 'svg',
src: http.basePath.prepend(
'/plugins/enterpriseSearch/assets/source_icons/google_cloud.svg'
'/plugins/enterpriseSearch/assets/source_icons/google_cloud_storage.svg'
),
},
],
@ -691,12 +697,13 @@ export const registerEnterpriseSearchIntegrations = (
}
),
categories: ['enterprise_search', 'elastic_stack', 'custom'],
uiInternalPath: '/app/enterprise_search/content/search_indices/new_index?method=connector',
uiInternalPath:
'/app/enterprise_search/content/search_indices/new_index/connector?service_type=azure_blob_storage',
icons: [
{
type: 'svg',
src: http.basePath.prepend(
'/plugins/enterpriseSearch/assets/source_icons/azure_blob.svg'
'/plugins/enterpriseSearch/assets/source_icons/azure_blob_storage.svg'
),
},
],

View file

@ -68,7 +68,7 @@ export const addConnector = async (
index_name: string;
is_native: boolean;
language: string | null;
service_type?: string | null;
service_type?: string;
}
): Promise<{ id: string; index_name: string }> => {
const connectorsIndexExists = await client.asCurrentUser.indices.exists({
@ -96,7 +96,7 @@ export const addConnector = async (
run_ml_inference: pipeline.default_run_ml_inference,
}
: null,
serviceType: input.service_type,
serviceType: input.service_type ?? null,
});
return await createConnector(document, client, input.language, !!input.delete_existing_connector);

View file

@ -20,7 +20,6 @@ import { ErrorCode } from '../../../common/types/error_codes';
import { addConnector } from '../../lib/connectors/add_connector';
import { fetchSyncJobsByConnectorId } from '../../lib/connectors/fetch_sync_jobs';
import { cancelSyncs } from '../../lib/connectors/post_cancel_syncs';
import { configureNativeConnector } from '../../lib/connectors/put_configure_native';
import { updateFiltering } from '../../lib/connectors/put_update_filtering';
import { updateFilteringDraft } from '../../lib/connectors/put_update_filtering_draft';
import { startConnectorSync } from '../../lib/connectors/start_sync';
@ -48,6 +47,7 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
index_name: schema.string(),
is_native: schema.boolean(),
language: schema.nullable(schema.string()),
service_type: schema.maybe(schema.string()),
}),
},
},
@ -276,23 +276,6 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
})
);
router.put(
{
path: '/internal/enterprise_search/connectors/{connectorId}/configure_native',
validate: {
body: schema.object({ service_type: schema.string() }),
params: schema.object({
connectorId: schema.string(),
}),
},
},
elasticsearchErrorHandler(log, async (context, request, response) => {
const { client } = (await context.core).elasticsearch;
await configureNativeConnector(client, request.params.connectorId, request.body.service_type);
return response.ok();
})
);
router.put(
{
path: '/internal/enterprise_search/connectors/{connectorId}/name_and_description',

View file

@ -22,6 +22,7 @@ describe('createConnectorDocument', () => {
reduce_whitespace: true,
run_ml_inference: false,
},
serviceType: null,
})
).toEqual({
api_key_id: null,
@ -114,6 +115,7 @@ describe('createConnectorDocument', () => {
reduce_whitespace: true,
run_ml_inference: false,
},
serviceType: null,
})
).toEqual({
api_key_id: null,

View file

@ -5,6 +5,9 @@
* 2.0.
*/
import { NATIVE_CONNECTOR_DEFINITIONS } from '../../common/connectors/native_connectors';
import { ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE } from '../../common/constants';
import {
ConnectorDocument,
ConnectorStatus,
@ -25,9 +28,31 @@ export function createConnectorDocument({
isNative: boolean;
language: string | null;
pipeline?: IngestPipelineParams | null;
serviceType?: string | null;
serviceType: string | null;
}): ConnectorDocument {
const currentTimestamp = new Date().toISOString();
const nativeConnector =
isNative && serviceType ? NATIVE_CONNECTOR_DEFINITIONS[serviceType] : undefined;
if (
isNative &&
serviceType &&
!nativeConnector &&
serviceType !== ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE
) {
throw new Error(`Could not find connector definition for service type ${serviceType}`);
}
const nativeFields = nativeConnector
? {
configuration: nativeConnector.configuration,
features: nativeConnector.features,
name: nativeConnector.name,
service_type: serviceType,
status: ConnectorStatus.NEEDS_CONFIGURATION,
}
: {};
return {
api_key_id: null,
configuration: {},
@ -100,5 +125,6 @@ export function createConnectorDocument({
service_type: serviceType || null,
status: ConnectorStatus.CREATED,
sync_now: false,
...nativeFields,
};
}

View file

@ -11170,17 +11170,10 @@
"xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "Pipeline d'inférence de Machine Learning \"{pipelineName}\" supprimé",
"xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "Pipeline d'inférence de Machine Learning détaché de \"{pipelineName}\"",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "Modifier ce pipeline à partir de {ingestPipelines} dans Gestion de la Suite",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "Recherchez dans votre contenu {name} avec Enterprise Search.",
"xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "Vous recherchez d'autres connecteurs ? {workplaceSearchLink} ou {buildYourOwnConnectorLink}.",
"xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "Votre index utilisera maintenant le connecteur natif {connectorName}.",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "Un index portant le nom {indexName} existe déjà",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName} n'est pas un nom d'index valide",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "Votre index sera nommé : {indexName}",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "Un index supprimé appelé {indexName} était, à l'origine, lié à une configuration de connecteur. Voulez-vous remplacer cette configuration de connecteur par la nouvelle ?",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "À l'aide de notre infrastructure de connecteur et de nos exemples de clients de connecteur, vous pouvez accélérer l'ingestion vers Elasticsearch {bulkApiDocLink} pour n'importe quelle source de données. Une fois l'index créé, le système vous guidera pour accéder à l'infrastructure du connecteur et connecter votre premier client.",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "Une fois que vous avez créé votre connecteur, votre contenu est prêt. Créez votre première expérience de recherche avec {elasticsearchLink} ou bien explorez les outils d'expérience de recherche fournis par {appSearchLink}. Nous vous conseillons de créer un {searchEngineLink} pour bénéficier du meilleur équilibre entre puissance et simplicité.",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "Fournissez un nom d'index unique et définissez éventuellement un {languageAnalyzerDocLink} pour l'index. Cet index contiendra le contenu de la source de données et il est optimisé avec les mappings de champ par défaut pour les expériences de recherche correspondantes.",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "Faites votre choix dans notre catalogue de connecteurs natifs pour commencer à extraire un contenu interrogeable de sources de données prises en charge telles que MongoDB. Si vous devez personnaliser le comportement d'un connecteur, vous pouvez toujours déployer la version client du connecteur autogéré et l'enregistrer via le workflow {buildAConnectorLabel}.",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "Affichage de {results} sur {total}. Nombre maximal de résultats de recherche de {maximum} documents.",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "Documents par page : {docPerPage}",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount} documents",
@ -12605,12 +12598,9 @@
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "Message d'erreur",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "Horodatage",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "Erreurs d'inférence",
"xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "créez-les vous-même",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "Documentation",
"xpack.enterpriseSearch.content.indices.selectConnector.description": "Lancez-vous en sélectionnant le connecteur que vous souhaiteriez configurer pour extraire, indexer et synchroniser les données à partir de votre source de données dans votre index de recherche nouvellement créé.",
"xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "Sélectionner et configurer",
"xpack.enterpriseSearch.content.indices.selectConnector.title": "Sélectionner un connecteur",
"xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "Afficher d'autres intégrations dans Workplace Search",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "Attacher",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "Créer un pipeline",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "Configurer",
@ -12640,38 +12630,17 @@
"xpack.enterpriseSearch.content.new_index.successToast.description": "Vous pouvez utiliser des moteurs App Search pour créer une expérience de recherche pour votre nouvel index Elasticsearch.",
"xpack.enterpriseSearch.content.new_index.successToast.title": "Index créé avec succès",
"xpack.enterpriseSearch.content.newIndex.breadcrumb": "Nouvel index de recherche",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "Ajouter des documents par programmation en se connectant avec l'API",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "Un peu de développement nécessaire",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "Utiliser lAPI",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "Utiliser le cadre des connecteurs pour créer rapidement des connecteurs pour des sources de données personnalisées",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "Développement nécessaire",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "Créer un connecteur",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "Découvrir, extraire, indexer et synchroniser tout le contenu de votre site web",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "Aucun développement nécessaire",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "Utiliser le robot d'indexation",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "Configurer un connecteur pour extraire, indexer et synchroniser tout votre contenu des sources de données prises en charge ",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "Aucun développement nécessaire",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "Utiliser un connecteur",
"xpack.enterpriseSearch.content.newIndex.emptyState.description": "Les données que vous ajoutez dans Enterprise Search sont appelées \"index de recherche\", et vous pouvez effectuer des recherches à l'intérieur à la fois dans App Search et dans Workplace Search. Maintenant, vous pouvez utiliser vos connecteurs dans App Search et vos robots d'indexation dans Workplace Search.",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "Lisez les documents",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "Vous souhaitez en savoir plus sur les index de recherche ?",
"xpack.enterpriseSearch.content.newIndex.emptyState.title": "Sélectionner une méthode d'ingestion",
"xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "Générez une clé dAPI et consultez la documentation pour envoyer des documents au point de terminaison de lAPI Elasticsearch. Des clients linguistiques sont disponibles pour une intégration simplifiée.",
"xpack.enterpriseSearch.content.newIndex.methodApi.title": "Indexer avec lAPI",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "API Bulk",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "Créer et configurer un connecteur",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "Configurer un connecteur",
"xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "Configurez les domaines que vous souhaitez indexer et, lorsque vous êtes prêt, déclenchez votre première indexation. Laissez Enterprise Search faire le reste.",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "Créer un index",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "La langue peut être modifiée ultérieurement, mais ce changement peut nécessiter une réindexation.",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "Analyseur linguistique",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "Les noms doivent être en minuscules et ne peuvent pas contenir d'espaces ni de caractères spéciaux.",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "Nom de l'index",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "Définir un nom pour votre index",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "Consulter la documentation",
"xpack.enterpriseSearch.content.newIndex.pageTitle": "Nouvel index de recherche",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "Créez un index Elasticsearch optimisé pour la recherche en sélectionnant une méthode d'ingestion adaptée à votre cas d'utilisation.",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "Sélectionner une méthode d'ingestion",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "Annuler",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "Remplacer la configuration",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "Remplacer un connecteur existant",
@ -12679,16 +12648,6 @@
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "Nous n'avons pas pu créer votre index",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "L'index existe déjà.",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "Vous n'êtes pas autorisé à créer ce connecteur",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "Créer un connecteur",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "moteur de recherche",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "Créer une expérience de recherche",
"xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "Configurer les paramètres dingestion",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "Indexer avec le robot d'indexation",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "analyseur linguistique",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "Créer un index Elasticsearch",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "Indexer à l'aide d'un connecteur",
"xpack.enterpriseSearch.content.newIndex.types.api": "Point de terminaison d'API",
"xpack.enterpriseSearch.content.newIndex.types.connector": "Connecteur",
"xpack.enterpriseSearch.content.newIndex.types.crawler": "Robot d'indexation",
@ -26574,9 +26533,6 @@
"xpack.reporting.diagnostic.browserMissingFonts": "Le navigateur n'a pas réussi à localiser de police par défaut. Consultez {url} pour corriger ce problème.",
"xpack.reporting.diagnostic.noUsableSandbox": "Impossible d'utiliser la sandbox Chromium. Vous pouvez la désactiver à vos risques et périls avec \"xpack.screenshotting.browser.chromium.disableSandbox\". Veuillez consulter {url}",
"xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "Impossible de déchiffrer les données de la tâche de reporting. Veuillez vous assurer que {encryptionKey} est défini et générez à nouveau ce rapport. {err}",
"generateCsv.esErrorMessage": "Réponse {statusCode} reçue d'Elasticsearch : {message}",
"generateCsv.incorrectRowCount": "Une erreur a été rencontrée avec le nombre de lignes CSV générées à partir de la recherche : {expected} prévues, {received} reçues.",
"generateCsv.unknownErrorMessage": "Une erreur inconnue est survenue : {message}",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "Désolé, vous n'êtes pas autorisé à afficher ou supprimer les rapports {jobtype}",
"xpack.reporting.jobsQuery.deleteError": "Impossible de supprimer le rapport : {error}",
"xpack.reporting.jobStatusDetail.attemptXofY": "Tentative {attempts} sur {max_attempts}.",
@ -26633,9 +26589,6 @@
"xpack.reporting.diagnostic.screenshotFailureMessage": "Impossible d'effectuer une capture d'écran de votre installation Kibana.",
"xpack.reporting.errorHandler.unknownError": "Erreur inconnue",
"xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "Les en-têtes de tâche sont manquants",
"generateCsv.authenticationExpired.partialResultsMessage": "Ce rapport contient des résultats CSV partiels, car le token d'authentification a expiré. Exportez une quantité moindre de données ou augmentez le délai d'expiration du token d'authentification.",
"generateCsv.csvUnableToClosePit": "Impossible de fermer le point temporel utilisé pour la recherche. Vérifiez les logs de serveur Kibana.",
"generateCsv.escapedFormulaValues": "Le CSV peut contenir des formules dont les valeurs sont précédées d'un caractère d'échappement",
"xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "Inconnu",
"xpack.reporting.jobResponse.errorHandler.unknownError": "Erreur inconnue",
"xpack.reporting.jobStatusDetail.deprecatedText": "Il s'agit d'un type d'exportation déclassé. L'automatisation de ce rapport devra être à nouveau créée pour une question de compatibilité avec les futures versions de Kibana.",
@ -26772,6 +26725,12 @@
"xpack.reporting.uiSettings.validate.customLogo.badFile": "Désolé, ce fichier ne convient pas. Veuillez essayer un autre fichier image.",
"xpack.reporting.uiSettings.validate.customLogo.tooLarge": "Désolé, ce fichier est trop volumineux. Le fichier image doit être inférieur à 200 kilo-octets.",
"xpack.reporting.userAccessError.learnMoreLink": "En savoir plus",
"generateCsv.esErrorMessage": "Réponse {statusCode} reçue d'Elasticsearch : {message}",
"generateCsv.incorrectRowCount": "Une erreur a été rencontrée avec le nombre de lignes CSV générées à partir de la recherche : {expected} prévues, {received} reçues.",
"generateCsv.unknownErrorMessage": "Une erreur inconnue est survenue : {message}",
"generateCsv.authenticationExpired.partialResultsMessage": "Ce rapport contient des résultats CSV partiels, car le token d'authentification a expiré. Exportez une quantité moindre de données ou augmentez le délai d'expiration du token d'authentification.",
"generateCsv.csvUnableToClosePit": "Impossible de fermer le point temporel utilisé pour la recherche. Vérifiez les logs de serveur Kibana.",
"generateCsv.escapedFormulaValues": "Le CSV peut contenir des formules dont les valeurs sont précédées d'un caractère d'échappement",
"xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}",
"xpack.rollupJobs.create.errors.idSameAsCloned": "Le nom doit être différent du nom cloné : \"{clonedId}\".",
"xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "Supprimez les caractères {characterList} de votre modèle d'indexation.",

View file

@ -11169,17 +11169,10 @@
"xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "機械学習推論パイプライン\"{pipelineName}\"を削除しました",
"xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "機械学習推論パイプラインを\"{pipelineName}\"からデタッチしました",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "スタック管理で{ingestPipelines}からこのパイプラインを編集",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "エンタープライズサーチで{name}コンテンツを検索します。",
"xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "その他のコネクターをお探しの場合は、{workplaceSearchLink}または{buildYourOwnConnectorLink}。",
"xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "インデックスは{connectorName}ネイティブコネクターを使用します。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "名前{indexName}のインデックスがすでに存在します",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName}は無効なインデックス名です",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "インデックスは次の名前になります:{indexName}",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "削除されたインデックス{indexName}は、既存のコネクター構成に関連付けられていました。既存のコネクター構成を新しいコネクター構成で置き換えますか?",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "コネクターフレームワークとコネクタークライアントの例を使用すると、あらゆるデータソースでElasticsearch {bulkApiDocLink}への取り込みを高速化できます。インデックスを作成した後、コネクターフレームワークにアクセスし、最初のコネクタークライアントを接続するための手順が示されます。",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "コネクターを作成すると、コンテンツの準備が完了します。{elasticsearchLink}で最初の検索エクスペリエンスを構築するか、{appSearchLink}で提供されている検索エクスペリエンスツールを使用します。柔軟性の高い能力とターンキーのシンプル性を両立するために、{searchEngineLink}を作成することをお勧めします。",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "一意のインデックス名を指定し、任意でインデックスのデフォルト{languageAnalyzerDocLink}を設定します。このインデックスには、データソースコンテンツが格納されます。また、デフォルトフィールドマッピングで最適化され、関連する検索エクスペリエンスを実現します。",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "ネイティブコネクターのカタログから選択し、MongoDBなどのサポートされているデータソースから検索可能なコンテンツの抽出を開始します。コネクターの動作をカスタマイズする必要がある場合は、セルフマネージドコネクタークライアントバージョンを常にデプロイし、{buildAConnectorLabel}ワークフロー経由で登録できます。",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "{total}件中{results}件を表示中。{maximum}ドキュメントが検索結果の最大数です。",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "毎秒あたりのドキュメント:{docPerPage}",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount}ドキュメント",
@ -12604,12 +12597,9 @@
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "エラーメッセージ",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "タイムスタンプ",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "推論エラー",
"xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "世界に1つ",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "ドキュメント",
"xpack.enterpriseSearch.content.indices.selectConnector.description": "まず、データソースから、新しく作成された検索インデックスに、データを抽出、インデックス、同期するように構成するコネクターを選択します。",
"xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "選択して構成",
"xpack.enterpriseSearch.content.indices.selectConnector.title": "コネクターを選択",
"xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "Workplace Searchで追加の統合を表示",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "接続",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "パイプラインの作成",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "構成",
@ -12639,38 +12629,17 @@
"xpack.enterpriseSearch.content.new_index.successToast.description": "App Searchエンジンを使用して、新しいElasticsearchインデックスの検索エクスペリエンスを構築できます。",
"xpack.enterpriseSearch.content.new_index.successToast.title": "インデックスが正常に作成されました",
"xpack.enterpriseSearch.content.newIndex.breadcrumb": "新しい検索インデックス",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "APIに接続してプログラムでドキュメントを追加",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "ある程度の開発が必要です",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "APIを使用",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "コネクターフレームワークを使用すると、カスタムデータソースのコネクターをすばやく構築できます。",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "開発が必要です",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "コネクターを作成",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "すべてのWebサイトコンテンツを検出、抽出、インデックス作成、同期",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "開発は不要です",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "Webクローラーを使用",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "サポートされているデータソースから、すべてのコンテンツを抽出、インデックス、同期するように、コネクターを設定 ",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "開発は不要です",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "コネクターを使用",
"xpack.enterpriseSearch.content.newIndex.emptyState.description": "エンタープライズ サーチで追加したデータは検索インデックスと呼ばれ、App SearchとWorkplace Searchの両方で検索可能です。App SearchのコネクターとWorkplace SearchのWebクローラーを使用できます。",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "<b>ドキュメント</b><b>を読む</b>",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "検索インデックスの詳細",
"xpack.enterpriseSearch.content.newIndex.emptyState.title": "インジェスチョン方法を選択",
"xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "APIキーを生成し、ドキュメントを参照して、ドキュメントをElasticsearch APIエンドポイントに送信します。合理化された統合では、言語クライアントを使用できます。",
"xpack.enterpriseSearch.content.newIndex.methodApi.title": "APIを使用してインデックス",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "Bulk API",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "コネクターを作成して構成",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "コネクターを構成",
"xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "クローリングするドメインを構成し、準備が完了したら、最初のクローリングをトリガーします。その後の処理は、エンタープライズ サーチで自動的に実行されます。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "インデックスの作成",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "言語は後から変更できますが、再インデックスが必要になる場合があります",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "言語アナライザー",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "名前は小文字で入力してください。スペースや特殊文字は使用できません。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "インデックス名",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "インデックスの名前を設定",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "ドキュメントを表示",
"xpack.enterpriseSearch.content.newIndex.pageTitle": "新しい検索インデックス",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "ユースケースのインジェスチョン方法を選択し、検索が最適化されたElasticsearchインデックスを作成します。",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "インジェスチョン方法を選択",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "キャンセル",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "構成を置換",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "既存のコネクターを置換",
@ -12678,16 +12647,6 @@
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "インデックスを作成できませんでした",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "このインデックスはすでに存在します",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "このコネクターを作成する権限がありません",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "コネクターを作成",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "検索エンジン",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "検索エクスペリエンスを構築",
"xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "インジェスチョン設定を構成",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "Webクローラーを使用してインデックス",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "言語アナライザー",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "Elasticsearchインデックスを作成",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "コネクターを使用したインデックス作成",
"xpack.enterpriseSearch.content.newIndex.types.api": "APIエンドポイント",
"xpack.enterpriseSearch.content.newIndex.types.connector": "コネクター",
"xpack.enterpriseSearch.content.newIndex.types.crawler": "Webクローラー",
@ -26555,9 +26514,6 @@
"xpack.reporting.diagnostic.browserMissingFonts": "ブラウザーはデフォルトフォントを検索できませんでした。この問題を修正するには、{url}を参照してください。",
"xpack.reporting.diagnostic.noUsableSandbox": "Chromiumサンドボックスを使用できません。これは「xpack.screenshotting.browser.chromium.disableSandbox」で無効にすることができます。この作業はご自身の責任で行ってください。{url}を参照してください",
"xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "レポートジョブデータの解読に失敗しました。{encryptionKey} が設定されていることを確認してこのレポートを再生成してください。{err}",
"generateCsv.esErrorMessage": "Elasticsearchから{statusCode}応答を受け取りました:{message}",
"generateCsv.incorrectRowCount": "検索から生成されたCSVの行数でエラーが発生しました。正しい行数{expected}、実際の行数:{received}。",
"generateCsv.unknownErrorMessage": "不明なエラーが発生しました:{message}",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "{jobtype}レポートを表示または削除する権限がありません",
"xpack.reporting.jobsQuery.deleteError": "レポートを削除できません:{error}",
"xpack.reporting.jobStatusDetail.attemptXofY": "{max_attempts}回中{attempts}回試行します。",
@ -26614,9 +26570,6 @@
"xpack.reporting.diagnostic.screenshotFailureMessage": "Kibanaインストールのスクリーンショットを作成できませんでした。",
"xpack.reporting.errorHandler.unknownError": "不明なエラー",
"xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "ジョブヘッダーがありません",
"generateCsv.authenticationExpired.partialResultsMessage": "認証トークが有効期限切れのため、このレポートには一部のCSVの結果が含まれています。少ない量のデータをエクスポートするか、認証トークンのタイムアウトを大きくします。",
"generateCsv.csvUnableToClosePit": "検索に使用したPoint-In-Timeを閉じることができません。Kibanaサーバーログを確認してください。",
"generateCsv.escapedFormulaValues": "CSVには、値がエスケープされた式が含まれる場合があります",
"xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "不明",
"xpack.reporting.jobResponse.errorHandler.unknownError": "不明なエラー",
"xpack.reporting.jobStatusDetail.deprecatedText": "これは廃止予定のエクスポートタイプです。将来のバージョンのKibanaとの互換性のためには、このレポートの自動化を再作成する必要があります。",
@ -26753,6 +26706,12 @@
"xpack.reporting.uiSettings.validate.customLogo.badFile": "このファイルは動作しません。別の画像ファイルを試してください。",
"xpack.reporting.uiSettings.validate.customLogo.tooLarge": "このファイルは大きすぎます。画像ファイルは200キロバイト未満でなければなりません。",
"xpack.reporting.userAccessError.learnMoreLink": "詳細",
"generateCsv.esErrorMessage": "Elasticsearchから{statusCode}応答を受け取りました:{message}",
"generateCsv.incorrectRowCount": "検索から生成されたCSVの行数でエラーが発生しました。正しい行数{expected}、実際の行数:{received}。",
"generateCsv.unknownErrorMessage": "不明なエラーが発生しました:{message}",
"generateCsv.authenticationExpired.partialResultsMessage": "認証トークが有効期限切れのため、このレポートには一部のCSVの結果が含まれています。少ない量のデータをエクスポートするか、認証トークンのタイムアウトを大きくします。",
"generateCsv.csvUnableToClosePit": "検索に使用したPoint-In-Timeを閉じることができません。Kibanaサーバーログを確認してください。",
"generateCsv.escapedFormulaValues": "CSVには、値がエスケープされた式が含まれる場合があります",
"xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}",
"xpack.rollupJobs.create.errors.idSameAsCloned": "名前はクローン名「{clonedId}」と同じにできません。",
"xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "インデックスパターンから {characterList} を削除してください。",

View file

@ -11170,17 +11170,10 @@
"xpack.enterpriseSearch.content.indices.pipelines.successToastDeleteMlPipeline.title": "已删除 Machine Learning 推理管道“{pipelineName}”",
"xpack.enterpriseSearch.content.indices.pipelines.successToastDetachMlPipeline.title": "已从“{pipelineName}”中分离 Machine Learning 推理管道",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.jsonConfigurations.unmanaged.description": "从 Stack Management 中的 {ingestPipelines} 编辑此管道",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.description": "使用 Enterprise Search 搜索您的 {name} 内容。",
"xpack.enterpriseSearch.content.indices.selectConnector.moreConnectorsMessage": "正在寻找更多连接器?{workplaceSearchLink}或{buildYourOwnConnectorLink}",
"xpack.enterpriseSearch.content.indices.selectConnector.successToast.title": "您的索引现在将使用 {connectorName} 本机连接器。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.alreadyExists.error": "名为 {indexName} 的索引已存在",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.isInvalid.error": "{indexName} 为无效索引名称",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineOne": "您的索引将命名为:{indexName}",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.description": "名为 {indexName} 的已删除索引最初绑定到现有连接器配置。是否要将现有连接器配置替换成新的?",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.content": "使用我们的连接器框架和连接器客户端示例,您将能够加速任何数据源的 Elasticsearch {bulkApiDocLink} 采集。创建索引后,将引导您完成各个步骤,以访问连接器框架并连接到您的首个连接器客户端。",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.content": "构建连接器后,您的内容将准备就绪。通过 {elasticsearchLink} 构建您的首次搜索体验,或浏览 {appSearchLink} 提供的搜索体验工具。我们建议您创建 {searchEngineLink},以实现灵活的功能与一站式简便性的完美平衡。",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.content": "提供唯一的索引名称,并为索引设置默认的 {languageAnalyzerDocLink}(可选)。此索引将存放您的数据源内容,并通过默认字段映射进行优化,以提供相关搜索体验。",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.content": "从我们的本机连接器目录中进行选择,以开始从 MongoDB 等受支持的数据源中提取可搜索内容。如果需要定制连接器的行为,您始终可以部署自我管理的连接器客户端版本并通过 {buildAConnectorLabel} 工作流进行注册。",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.description": "显示 {results} 个,共 {total} 个。搜索结果最多包含 {maximum} 个文档。",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.pagination.itemsPerPage": "每页文档数:{docPerPage}",
"xpack.enterpriseSearch.content.searchIndex.documents.documentList.paginationOptions.option": "{docCount} 个文档",
@ -12605,12 +12598,9 @@
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.message": "错误消息",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.tableColumn.timestamp": "时间戳",
"xpack.enterpriseSearch.content.indices.pipelines.tabs.pipelineInferenceLogs.title": "推理错误",
"xpack.enterpriseSearch.content.indices.selectConnector.buildYourOwnConnectorLinkLabel": "自行构建",
"xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel": "文档",
"xpack.enterpriseSearch.content.indices.selectConnector.description": "通过选择要配置以从数据源中提取、索引数据并将其同步到您新建的搜索索引的连接器,从而开始使用。",
"xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel": "选择并配置",
"xpack.enterpriseSearch.content.indices.selectConnector.title": "选择连接器",
"xpack.enterpriseSearch.content.indices.selectConnector.workplaceSearchLinkLabel": "在 Workplace Search 中查看其他集成",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.attach": "附加",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create": "创建管道",
"xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.steps.configure.title": "配置",
@ -12640,38 +12630,17 @@
"xpack.enterpriseSearch.content.new_index.successToast.description": "您可以使用 App Search 引擎为您的新 Elasticsearch 索引构建搜索体验。",
"xpack.enterpriseSearch.content.new_index.successToast.title": "已成功创建索引",
"xpack.enterpriseSearch.content.newIndex.breadcrumb": "新搜索索引",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.description": "通过连接 API 以编程方式添加文档",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.footer": "需要一些开发",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.api.label": "使用 API",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.description": "使用连接器框架为定制数据源快速构建连接器",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.footer": "需要开发",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.connector.label": "构建连接器",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.description": "发现、提取、索引和同步所有网站内容",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.footer": "无需开发",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.crawler.label": "使用网络爬虫",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.description": "配置连接器以从受支持的数据源提取、索引和同步所有内容 ",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.footer": "无需开发",
"xpack.enterpriseSearch.content.newIndex.buttonGroup.nativeConnector.label": "使用连接器",
"xpack.enterpriseSearch.content.newIndex.emptyState.description": "您在 Enterprise Search 中添加的数据称为搜索索引,您可在 App Search 和 Workplace Search 中搜索这些数据。现在,您可以在 App Search 中使用连接器,在 Workplace Search 中使用网络爬虫。",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.link": "阅读文档",
"xpack.enterpriseSearch.content.newIndex.emptyState.footer.title": "想要详细了解搜索索引?",
"xpack.enterpriseSearch.content.newIndex.emptyState.title": "选择采集方法",
"xpack.enterpriseSearch.content.newIndex.methodApi.steps.configureIngestion.content": "生成 API 密钥并查看文档,以便将文档发布到 Elasticsearch API 终端。语言客户端可用于精简集成。",
"xpack.enterpriseSearch.content.newIndex.methodApi.title": "使用 API 编制索引",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.bulkAPILink": "批量 API",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.buildConnector.title": "构建并配置连接器",
"xpack.enterpriseSearch.content.newIndex.methodConnector.steps.nativeConnector.title": "配置连接器",
"xpack.enterpriseSearch.content.newIndex.methodCrawler.steps.configureIngestion.content": "配置您要爬网的域,并在准备就绪后触发第一次爬网。让 Enterprise Search 执行其余操作。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.createIndex.buttonText": "创建索引",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputHelpText": "可以在稍后更改语言,但可能需要重新索引",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.languageInputLabel": "语言分析器",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputHelpText.lineTwo": "名称应为小写,并且不能包含空格或特殊字符。",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputLabel": "索引名称",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder": "设置索引的名称",
"xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.viewDocumentation.linkText": "查看文档",
"xpack.enterpriseSearch.content.newIndex.pageTitle": "新搜索索引",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.description": "通过为您的用例选择采集方法来创建搜索优化的 Elasticsearch 索引。",
"xpack.enterpriseSearch.content.newIndex.selectSearchIndex.title": "选择采集方法",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.cancelButton.label": "取消",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.confirmButton.label": "替换配置",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.confirmModal.title": "替换现有连接器",
@ -12679,16 +12648,6 @@
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.genericError": "无法创建您的索引",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.indexAlreadyExists": "此索引已存在",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.error.unauthorizedError": "您无权创建此连接器",
"xpack.enterpriseSearch.content.newIndex.steps.buildConnector.title": "构建连接器",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.appSearchLink": "App Search",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.elasticsearchLink": "Elasticsearch",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.searchEngineLink": "搜索引擎",
"xpack.enterpriseSearch.content.newIndex.steps.buildSearchExperience.title": "构建搜索体验",
"xpack.enterpriseSearch.content.newIndex.steps.configureIngestion.title": "配置采集设置",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.crawler.title": "使用网络爬虫编制索引",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.languageAnalyzerLink": "语言分析器",
"xpack.enterpriseSearch.content.newIndex.steps.createIndex.title": "创建 Elasticsearch 索引",
"xpack.enterpriseSearch.content.newIndex.steps.nativeConnector.title": "使用连接器进行索引",
"xpack.enterpriseSearch.content.newIndex.types.api": "API 终端",
"xpack.enterpriseSearch.content.newIndex.types.connector": "连接器",
"xpack.enterpriseSearch.content.newIndex.types.crawler": "网络爬虫",
@ -26571,9 +26530,6 @@
"xpack.reporting.diagnostic.browserMissingFonts": "浏览器找不到默认字体。请参见 {url} 以解决此问题。",
"xpack.reporting.diagnostic.noUsableSandbox": "无法使用 Chromium 沙盒。您自行承担使用“xpack.screenshotting.browser.chromium.disableSandbox”禁用此项的风险。请参见 {url}",
"xpack.reporting.exportTypes.common.failedToDecryptReportJobDataErrorMessage": "无法解密报告作业数据。请确保已设置 {encryptionKey},然后重新生成此报告。{err}",
"generateCsv.esErrorMessage": "从 Elasticsearch 收到 {statusCode} 响应:{message}",
"generateCsv.incorrectRowCount": "从搜索生成的 CSV 行数出现错误:应为 {expected},但收到 {received}。",
"generateCsv.unknownErrorMessage": "出现未知错误:{message}",
"xpack.reporting.jobResponse.errorHandler.notAuthorized": "抱歉,您无权查看或删除 {jobtype} 报告",
"xpack.reporting.jobsQuery.deleteError": "无法删除报告:{error}",
"xpack.reporting.jobStatusDetail.attemptXofY": "尝试 {attempts} 次,最多可尝试 {max_attempts} 次。",
@ -26630,9 +26586,6 @@
"xpack.reporting.diagnostic.screenshotFailureMessage": "我们无法拍摄 Kibana 安装的屏幕截图。",
"xpack.reporting.errorHandler.unknownError": "未知错误",
"xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage": "作业标头缺失",
"generateCsv.authenticationExpired.partialResultsMessage": "此报告包含部分 CSV 结果,因为身份验证令牌已过期。导出更少量的数据,或增加身份验证令牌的超时时间。",
"generateCsv.csvUnableToClosePit": "无法关闭用于搜索的时间点。查看 Kibana 服务器日志。",
"generateCsv.escapedFormulaValues": "CSV 可能包含值已转义的公式",
"xpack.reporting.jobCreatedBy.unknownUserPlaceholderText": "未知",
"xpack.reporting.jobResponse.errorHandler.unknownError": "未知错误",
"xpack.reporting.jobStatusDetail.deprecatedText": "这是已弃用的导出类型。此报告的自动化将需要重新创建,才能与未来版本的 Kibana 兼容。",
@ -26769,6 +26722,12 @@
"xpack.reporting.uiSettings.validate.customLogo.badFile": "抱歉,该文件无效。请尝试其他图像文件。",
"xpack.reporting.uiSettings.validate.customLogo.tooLarge": "抱歉,该文件过大。图像文件必须小于 200 千字节。",
"xpack.reporting.userAccessError.learnMoreLink": "了解详情",
"generateCsv.esErrorMessage": "从 Elasticsearch 收到 {statusCode} 响应:{message}",
"generateCsv.incorrectRowCount": "从搜索生成的 CSV 行数出现错误:应为 {expected},但收到 {received}。",
"generateCsv.unknownErrorMessage": "出现未知错误:{message}",
"generateCsv.authenticationExpired.partialResultsMessage": "此报告包含部分 CSV 结果,因为身份验证令牌已过期。导出更少量的数据,或增加身份验证令牌的超时时间。",
"generateCsv.csvUnableToClosePit": "无法关闭用于搜索的时间点。查看 Kibana 服务器日志。",
"generateCsv.escapedFormulaValues": "CSV 可能包含值已转义的公式",
"xpack.rollupJobs.create.errors.dateHistogramIntervalInvalidCalendarIntervalSuggestion": "1{unit}",
"xpack.rollupJobs.create.errors.idSameAsCloned": "名称不能与克隆名称相同:“{clonedId}。",
"xpack.rollupJobs.create.errors.indexPatternIllegalCharacters": "从索引模式中删除 {characterList} 字符。",