[Enterprise Search] Adds instrumentation for click telemetry (#143184)

* [Enterprise Search] Add click telemetry instrumentation to Content app

* Add some telemetry guidelines

* Remove callout from search_index.tsx

* Fix a few texts that were checking HTML attributes

* Remove unused translations

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sander Philipse 2022-10-13 10:21:56 +02:00 committed by GitHub
parent 670dd60d9a
commit 7c5235c2e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 307 additions and 196 deletions

View file

@ -0,0 +1,20 @@
# Telemetry
We have three forms of Telemetry in Kibana Enterprise Search:
1. Automatic URL navigation tracking
2. Automatic click tracking
3. Manual telemetry tracking
Automatic URL navigation tracking happens automatically and doesn't require any work from our side.
Click tracking also happens automatically, but works by sending the DOM tree location of the clicked element. That's only useful if that element has a good click tracking id. To facilitate this, we add a `data-telemetry-id` attribute to each button and other relevant elements. The telemetry is formatted like so:
`{app}-{ingestion type if applicable}-{page title}-{page subtitle}-{element title}`
`entSearchContent-connector-overview-generateApiKey-optimizedRequest`
You don't need to stick to this format exactly and you can deviate with the three titles if they don't make sense.
We also have a [telemetry endpoint](server/routes/enterprise_search/telemetry.ts) that can be used to create custom tracking counters, which are saved to Kibana's saved objects and periodically sent to the telemetry cluster. We can use this endpoint to facilitate more specific, customized telemetry needs.
Keep in mind that tracking and telemetry can be privacy and security sensitive, and we want to make sure we only send generic data to the telemetry cluster. For example, we should not be tracking the contents of form fields or index names.

View file

@ -37,6 +37,7 @@ export const AuthenticationPanelActions: React.FC = () => {
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-save"
iconType="checkInCircleFilled"
size="s"
color="primary"
@ -47,6 +48,7 @@ export const AuthenticationPanelActions: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-cancel"
iconType="crossInACircleFilled"
size="s"
color="danger"
@ -58,6 +60,7 @@ export const AuthenticationPanelActions: React.FC = () => {
</EuiFlexGroup>
) : currentAuth === null ? (
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-addCredentials"
color="success"
iconType="plusInCircle"
size="s"
@ -72,6 +75,7 @@ export const AuthenticationPanelActions: React.FC = () => {
</EuiButton>
) : (
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-deleteCredentials"
color="primary"
size="s"
onClick={() => {

View file

@ -40,6 +40,7 @@ export const AuthenticationPanelEditContent: React.FC = () => {
<EuiFlexGroup direction="row">
<EuiFlexItem>
<EuiCheckableCard
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-basicAuthentication"
id="basicAuthenticationCheckableCard"
className="authenticationCheckable"
label={
@ -72,6 +73,7 @@ export const AuthenticationPanelEditContent: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem>
<EuiCheckableCard
data-telemetry-id="entSearchContent-crawler-domainDetail-authentication-authenticationHeader"
id="authenticationHeaderCheckableCard"
className="authenticationCheckable"
label={

View file

@ -126,6 +126,7 @@ export const CrawlRulesTable: React.FC<CrawlRulesTableProps> = ({
{
editingRender: (crawlRule, onChange, { isInvalid, isLoading }) => (
<EuiSelect
data-telemetry-id="entSearchContent-crawler-domainDetail-crawlRules-policy"
fullWidth
hasNoInitialSelection
value={(crawlRule as CrawlRule).policy}
@ -151,6 +152,7 @@ export const CrawlRulesTable: React.FC<CrawlRulesTableProps> = ({
{
editingRender: (crawlRule, onChange, { isInvalid, isLoading }) => (
<EuiSelect
data-telemetry-id="entSearchContent-crawler-domainDetail-crawlRules-rule"
fullWidth
hasNoInitialSelection
value={(crawlRule as CrawlRule).rule}

View file

@ -61,6 +61,7 @@ export const CrawlerDomainDetail: React.FC = () => {
rightSideItems: [
<CrawlerStatusIndicator />,
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainDetail-header-deleteDomain"
isLoading={getLoading}
color="danger"
onClick={() => {
@ -78,6 +79,7 @@ export const CrawlerDomainDetail: React.FC = () => {
>
<CrawlerStatusBanner />
<EuiButtonTo
data-telemetry-id="entSearchContent-crawler-domainDetail-header-allDomains"
size="s"
color="text"
iconType="arrowLeft"

View file

@ -63,6 +63,7 @@ export const DeduplicationPanel: React.FC = () => {
}
action={
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainDetail-deduplication-reset"
color="warning"
iconType="refresh"
size="s"
@ -99,7 +100,13 @@ export const DeduplicationPanel: React.FC = () => {
}
>
<EuiSwitch
label="Prevent duplicate documents"
data-telemetry-id="entSearchContent-crawler-domainDetail-deduplication-preventDuplicates"
label={i18n.translate(
'xpack.enterpriseSearch.crawler.deduplicationPanel.preventDuplicateLabel',
{
defaultMessage: 'Prevent duplicate documents',
}
)}
checked={deduplicationEnabled}
onChange={() =>
deduplicationEnabled
@ -125,6 +132,7 @@ export const DeduplicationPanel: React.FC = () => {
<EuiPopover
button={
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-domainDetail-deduplication-selectFields"
size="xs"
iconType="arrowDown"
iconSide="right"
@ -155,6 +163,7 @@ export const DeduplicationPanel: React.FC = () => {
<EuiContextMenuPanel
items={[
<EuiContextMenuItem
data-telemetry-id="entSearchContent-crawler-domainDetail-deduplication-showAllFields"
key="all fields"
icon={showAllFields ? 'check' : 'empty'}
onClick={() => {
@ -170,6 +179,7 @@ export const DeduplicationPanel: React.FC = () => {
)}
</EuiContextMenuItem>,
<EuiContextMenuItem
data-telemetry-id="entSearchContent-crawler-domainDetail-deduplication-showSelectedFields"
key="selected fields"
icon={showAllFields ? 'empty' : 'check'}
onClick={() => {

View file

@ -37,6 +37,7 @@ export const EntryPointsTable: React.FC<EntryPointsTableProps> = ({ domain, inde
{
editingRender: (entryPoint, onChange, { isInvalid, isLoading }) => (
<EuiFieldText
data-telemetry-id="entSearchContent-crawler-domainDetail-entryPoints-editEntryPoint"
fullWidth
value={(entryPoint as EntryPoint)[field]}
onChange={(e) => onChange(e.target.value)}

View file

@ -41,6 +41,7 @@ export const SitemapsTable: React.FC<SitemapsTableProps> = ({ domain, indexName,
{
editingRender: (sitemap, onChange, { isInvalid, isLoading }) => (
<EuiFieldText
data-telemetry-id="entSearchContent-crawler-domainDetail-siteMaps-editSitemap"
fullWidth
value={(sitemap as Sitemap)[field]}
onChange={(e) => onChange(e.target.value)}

View file

@ -62,7 +62,6 @@ type NewSearchIndexActions = Pick<
crawlerIndexCreated: Actions<CreateCrawlerIndexArgs, CreateCrawlerIndexResponse>['apiSuccess'];
setLanguageSelectValue(language: string): { language: string };
setRawName(rawName: string): { rawName: string };
showIndexCreatedCallout: () => void;
};
export const NewSearchIndexLogic = kea<MakeLogicType<NewSearchIndexValues, NewSearchIndexActions>>({

View file

@ -42,14 +42,15 @@ export interface Props {
}
export const NewSearchIndexTemplate: React.FC<Props> = ({
buttonLoading,
children,
disabled,
docsUrl,
error,
title,
onNameChange,
onSubmit,
buttonLoading,
title,
type,
}) => {
const {
fullIndexName,
@ -141,6 +142,7 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
fullWidth
>
<EuiFieldText
data-telemetry-id={`entSearchContent-${type}-newIndex-editName`}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.newIndex.newSearchIndexTemplate.nameInputPlaceholder',
{
@ -183,6 +185,7 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
)}
>
<EuiSelect
data-telemetry-id={`entSearchContent-${type}-newIndex-languageAnalyzer`}
disabled={disabled}
options={SUPPORTED_LANGUAGES}
onChange={handleLanguageChange}
@ -197,6 +200,7 @@ export const NewSearchIndexTemplate: React.FC<Props> = ({
<EuiFlexGroup direction="row" alignItems="center" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id={`entSearchContent-${type}-newIndex-createIndex`}
fill
isDisabled={!rawName || buttonLoading || formInvalid || disabled}
isLoading={buttonLoading}

View file

@ -24,6 +24,7 @@ import { i18n } from '@kbn/i18n';
import { icons } from '../../../../../../assets/client_libraries';
import { docLinks } from '../../../../../shared/doc_links';
import { IndexViewLogic } from '../../index_view_logic';
import { OverviewLogic } from '../../overview.logic';
const libraries = [
@ -112,6 +113,7 @@ const libraries = [
export const ClientLibrariesPopover: React.FC = () => {
const { isClientsPopoverOpen } = useValues(OverviewLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { toggleClientsPopover } = useActions(OverviewLogic);
return (
@ -119,7 +121,12 @@ export const ClientLibrariesPopover: React.FC = () => {
isOpen={isClientsPopoverOpen}
closePopover={toggleClientsPopover}
button={
<EuiButton iconType="arrowDown" iconSide="right" onClick={toggleClientsPopover}>
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-clientLibraries-openClientLibraries`}
iconType="arrowDown"
iconSide="right"
onClick={toggleClientsPopover}
>
{i18n.translate(
'xpack.enterpriseSearch.content,overview.documentExample.clientLibraries.label',
{ defaultMessage: 'Client Libraries' }

View file

@ -31,6 +31,7 @@ import { Result } from '../../../../../shared/result/result';
import { resultMetaData } from '../../../../../shared/result/result_metadata';
import { DocumentsLogic } from '../../documents_logic';
import { IndexViewLogic } from '../../index_view_logic';
export const DocumentList: React.FC = () => {
const {
@ -40,6 +41,7 @@ export const DocumentList: React.FC = () => {
results,
simplifiedMapping: mappings,
} = useValues(DocumentsLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { onPaginate, setDocsPerPage } = useActions(DocumentsLogic);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
@ -111,6 +113,7 @@ export const DocumentList: React.FC = () => {
)}
button={
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-documents-docsPerPage`}
size="s"
iconType="arrowDown"
iconSide="right"

View file

@ -34,6 +34,8 @@ import { i18n } from '@kbn/i18n';
import { docLinks } from '../../../../../shared/doc_links';
import { IndexViewLogic } from '../../index_view_logic';
import { GenerateApiKeyModalLogic } from './generate_api_key_modal.logic';
interface GenerateApiKeyModalProps {
@ -43,6 +45,7 @@ interface GenerateApiKeyModalProps {
export const GenerateApiKeyModal: React.FC<GenerateApiKeyModalProps> = ({ indexName, onClose }) => {
const { keyName, apiKey, isLoading, isSuccess } = useValues(GenerateApiKeyModalLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { setKeyName, makeRequest } = useActions(GenerateApiKeyModalLogic);
return (
@ -81,6 +84,7 @@ export const GenerateApiKeyModal: React.FC<GenerateApiKeyModalProps> = ({ indexN
<EuiFlexItem>
<EuiFormRow label="Name your API key" fullWidth>
<EuiFieldText
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKey-editName`}
fullWidth
placeholder="Type a name for your API key"
onChange={(event) => setKeyName(event.currentTarget.value)}
@ -91,6 +95,7 @@ export const GenerateApiKeyModal: React.FC<GenerateApiKeyModalProps> = ({ indexN
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKey-generate `}
data-test-subj="generateApiKeyButton"
iconSide="left"
iconType="plusInCircle"
@ -130,6 +135,7 @@ export const GenerateApiKeyModal: React.FC<GenerateApiKeyModalProps> = ({ indexN
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKey-download`}
aria-label={i18n.translate(
'xpack.enterpriseSearch.content.overview.generateApiKeyModal.csvDownloadButton',
{ defaultMessage: 'Download API key' }
@ -166,7 +172,10 @@ export const GenerateApiKeyModal: React.FC<GenerateApiKeyModalProps> = ({ indexN
</>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={onClose}>
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKey-cancel`}
onClick={onClose}
>
{i18n.translate('xpack.enterpriseSearch.content.overview.generateApiKeyModal.cancel', {
defaultMessage: 'Cancel',
})}

View file

@ -19,11 +19,13 @@ import { KibanaLogic } from '../../../../../shared/kibana';
export interface CreateEngineMenuItemProps {
indexName?: string;
ingestionMethod: string;
isHiddenIndex?: boolean;
}
export const CreateEngineMenuItem: React.FC<CreateEngineMenuItemProps> = ({
indexName,
ingestionMethod,
isHiddenIndex,
}) => {
const engineCreationPath = !indexName
@ -37,6 +39,7 @@ export const CreateEngineMenuItem: React.FC<CreateEngineMenuItemProps> = ({
<EuiFlexGroup alignItems="center" gutterSize="xs">
<EuiFlexItem>
<EuiContextMenuItem
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-createEngine-createEngine`}
size="s"
icon="plusInCircle"
onClick={() => {

View file

@ -8,15 +8,22 @@
import React from 'react';
import { ElasticsearchIndexWithIngestion } from '../../../../../../../common/types/indices';
import { isCrawlerIndex, isConnectorIndex } from '../../../../utils/indices';
import { isCrawlerIndex, isConnectorIndex, getIngestionMethod } from '../../../../utils/indices';
import { CrawlerStatusIndicator } from '../../../shared/crawler_status_indicator/crawler_status_indicator';
import { SearchEnginesPopover } from './search_engines_popover';
import { SyncButton } from './sync_button';
// Used to populate rightSideItems of an EuiPageTemplate, which is rendered right-to-left
export const getHeaderActions = (indexData?: ElasticsearchIndexWithIngestion) => [
...(isCrawlerIndex(indexData) ? [<CrawlerStatusIndicator />] : []),
...(isConnectorIndex(indexData) ? [<SyncButton />] : []),
<SearchEnginesPopover indexName={indexData?.name} isHiddenIndex={indexData?.hidden} />,
];
export const getHeaderActions = (indexData?: ElasticsearchIndexWithIngestion) => {
const ingestionMethod = getIngestionMethod(indexData);
return [
...(isCrawlerIndex(indexData) ? [<CrawlerStatusIndicator />] : []),
...(isConnectorIndex(indexData) ? [<SyncButton />] : []),
<SearchEnginesPopover
indexName={indexData?.name}
ingestionMethod={ingestionMethod}
isHiddenIndex={indexData?.hidden}
/>,
];
};

View file

@ -28,11 +28,13 @@ import { SearchEnginesPopoverLogic } from './search_engines_popover_logic';
export interface SearchEnginesPopoverProps {
indexName?: string;
ingestionMethod: string;
isHiddenIndex?: boolean;
}
export const SearchEnginesPopover: React.FC<SearchEnginesPopoverProps> = ({
indexName,
ingestionMethod,
isHiddenIndex,
}) => {
const { isSearchEnginesPopoverOpen } = useValues(SearchEnginesPopoverLogic);
@ -43,7 +45,12 @@ export const SearchEnginesPopover: React.FC<SearchEnginesPopoverProps> = ({
isOpen={isSearchEnginesPopoverOpen}
closePopover={toggleSearchEnginesPopover}
button={
<EuiButton iconSide="right" iconType="arrowDown" onClick={toggleSearchEnginesPopover}>
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-searchEngines`}
iconSide="right"
iconType="arrowDown"
onClick={toggleSearchEnginesPopover}
>
{i18n.translate('xpack.enterpriseSearch.content.index.searchEngines.label', {
defaultMessage: 'Search engines',
})}
@ -54,6 +61,7 @@ export const SearchEnginesPopover: React.FC<SearchEnginesPopoverProps> = ({
size="s"
items={[
<EuiContextMenuItem
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-searchEngines-viewEngines`}
icon="eye"
onClick={() => {
KibanaLogic.values.navigateToUrl(APP_SEARCH_PLUGIN.URL, {
@ -78,10 +86,18 @@ export const SearchEnginesPopover: React.FC<SearchEnginesPopoverProps> = ({
}
)}
>
<CreateEngineMenuItem indexName={indexName} isHiddenIndex={isHiddenIndex} />
<CreateEngineMenuItem
indexName={indexName}
ingestionMethod={ingestionMethod}
isHiddenIndex={isHiddenIndex}
/>
</EuiToolTip>
) : (
<CreateEngineMenuItem indexName={indexName} isHiddenIndex={isHiddenIndex} />
<CreateEngineMenuItem
indexName={indexName}
ingestionMethod={ingestionMethod}
isHiddenIndex={isHiddenIndex}
/>
),
]}
/>

View file

@ -16,7 +16,8 @@ import { IngestionStatus } from '../../../../types';
import { IndexViewLogic } from '../../index_view_logic';
export const SyncButton: React.FC = () => {
const { ingestionStatus, isSyncing, isWaitingForSync } = useValues(IndexViewLogic);
const { ingestionMethod, ingestionStatus, isSyncing, isWaitingForSync } =
useValues(IndexViewLogic);
const { startSync } = useActions(IndexViewLogic);
const getSyncButtonText = () => {
@ -39,6 +40,7 @@ export const SyncButton: React.FC = () => {
};
return (
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-header-syncNow-startSync`}
onClick={startSync}
fill
disabled={ingestionStatus === IngestionStatus.INCOMPLETE}

View file

@ -1,78 +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 { useActions } from 'kea';
import {
EuiButton,
EuiLink,
EuiText,
EuiCallOut,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { IndexCreatedCalloutLogic } from './callout_logic';
interface IndexCreatedCalloutProps {
indexName: string;
}
export const IndexCreatedCallout: React.FC<IndexCreatedCalloutProps> = ({ indexName }) => {
const { dismissIndexCreatedCallout } = useActions(IndexCreatedCalloutLogic);
return (
<EuiCallOut
color="success"
iconType="check"
title={i18n.translate('xpack.enterpriseSearch.content.index.indexCreatedCallout.title', {
defaultMessage: 'Elasticsearch index created successfully',
})}
>
<EuiText size="m">
{i18n.translate('xpack.enterpriseSearch.content.index.indexCreatedCallout.info', {
defaultMessage:
'You can use App Search engines to build a search experience for your new Elasticsearch index.',
})}
<EuiLink external href={/* TODO */ '#'}>
{i18n.translate('xpack.enterpriseSearch.content.index.readDocumentation.link', {
defaultMessage: 'Read the documentation',
})}
</EuiLink>
</EuiText>
<EuiSpacer />
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton
fill
color="success"
onClick={() => {
// TODO bind it to AppSearch
// eslint-disable-next-line no-console
console.log(indexName);
}}
>
{i18n.translate('xpack.enterpriseSearch.content.index.createAppSearchEngine.button', {
defaultMessage: 'Create an App Search engine',
})}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton color="success" iconType="cross" onClick={dismissIndexCreatedCallout}>
{i18n.translate('xpack.enterpriseSearch.content.index.dismiss.button', {
defaultMessage: 'Dismiss',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiCallOut>
);
};

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 { kea, MakeLogicType } from 'kea';
interface IndexCreatedCalloutLogicValues {
isCalloutVisible: boolean;
}
interface IndexCreatedCalloutLogicActions {
dismissIndexCreatedCallout: void;
showIndexCreatedCallout: void;
}
export const IndexCreatedCalloutLogic = kea<
MakeLogicType<IndexCreatedCalloutLogicValues, IndexCreatedCalloutLogicActions>
>({
actions: {
dismissIndexCreatedCallout: true,
showIndexCreatedCallout: true,
},
path: ['enterprise_search', 'search_index', 'index_created_callout'],
reducers: () => ({
isCalloutVisible: [
false,
{
dismissIndexCreatedCallout: () => false,
showIndexCreatedCallout: () => true,
},
],
}),
});

View file

@ -21,10 +21,12 @@ import { i18n } from '@kbn/i18n';
import { KibanaLogic } from '../../../../../shared/kibana';
import { IndexViewLogic } from '../../index_view_logic';
import { OverviewLogic } from '../../overview.logic';
export const ManageKeysPopover: React.FC = () => {
const { isManageKeysPopoverOpen } = useValues(OverviewLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { toggleManageApiKeyPopover, openGenerateModal } = useActions(OverviewLogic);
return (
@ -44,6 +46,7 @@ export const ManageKeysPopover: React.FC = () => {
size="s"
items={[
<EuiContextMenuItem
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKeys-viewApiKeys`}
icon="eye"
onClick={() =>
KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', {
@ -60,7 +63,11 @@ export const ManageKeysPopover: React.FC = () => {
</p>
</EuiText>
</EuiContextMenuItem>,
<EuiContextMenuItem icon="plusInCircle" onClick={openGenerateModal}>
<EuiContextMenuItem
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKeys-createNewApiKey`}
icon="plusInCircle"
onClick={openGenerateModal}
>
<EuiText>
<p>
{i18n.translate(

View file

@ -216,6 +216,7 @@ export const ConnectorConfiguration: React.FC = () => {
)}
<EuiSpacer size="s" />
<EuiButton
data-telemetry-id="entSearchContent-connector-configuration-recheckNow"
iconType="refresh"
onClick={() => recheckIndex()}
isLoading={recheckIndexLoading}
@ -274,6 +275,7 @@ export const ConnectorConfiguration: React.FC = () => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonTo
data-telemetry-id="entSearchContent-connector-configuration-setScheduleAndSync"
to={`${generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName,
tabId: SearchIndexTabId.SCHEDULING,

View file

@ -40,7 +40,10 @@ export const ConnectorConfigurationConfig: React.FC = ({ children }) => {
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setIsEditing(!isEditing)}>
<EuiButton
data-telemetry-id="entSearchContent-connector-overview-configuration-editConfiguration"
onClick={() => setIsEditing(!isEditing)}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.config.editButton.title',
{

View file

@ -55,7 +55,11 @@ export const ConnectorConfigurationForm = () => {
<EuiFormRow>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton type="submit" isLoading={status === Status.LOADING}>
<EuiButton
data-telemetry-id="entSearchContent-connector-configuration-saveConfiguration"
type="submit"
isLoading={status === Status.LOADING}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.configurationConnector.config.submitButton.title',
{
@ -66,6 +70,7 @@ export const ConnectorConfigurationForm = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-connector-configuration-cancelEdit"
isDisabled={status === Status.LOADING}
onClick={() => {
setIsEditing(false);

View file

@ -76,12 +76,17 @@ export const ConnectorNameAndDescriptionForm: React.FC = () => {
<EuiFormRow>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton type="submit" isLoading={status === Status.LOADING}>
<EuiButton
data-telemetry-id="entSearchContent-connector-configuration-nameAndDescription-save"
type="submit"
isLoading={status === Status.LOADING}
>
{SAVE_BUTTON_LABEL}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-connector-configuration-nameAndDescription-cancel"
isDisabled={status === Status.LOADING}
onClick={() => {
setIsEditing(false);

View file

@ -92,6 +92,7 @@ export const ConnectorSchedulingComponent: React.FC = () => {
</EuiText>
<EuiSpacer size="s" />
<EuiButtonTo
data-telemetry-id="entSearchContent-connector-scheduling-configure"
to={generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName: index.name,
tabId: SearchIndexTabId.CONFIGURATION,
@ -161,6 +162,7 @@ export const ConnectorSchedulingComponent: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem>
<CronEditor
data-telemetry-id="entSearchContent-connector-scheduling-editSchedule"
fieldToPreferredValueMap={fieldToPreferredValueMap}
cronExpression={simpleCron.expression}
frequency={simpleCron.frequency}
@ -184,6 +186,7 @@ export const ConnectorSchedulingComponent: React.FC = () => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-connector-scheduling-resetSchedule"
disabled={!hasChanges || status === Status.LOADING}
onClick={() => {
setScheduling(schedulingInput);
@ -204,6 +207,7 @@ export const ConnectorSchedulingComponent: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id="entSearchContent-connector-scheduling-saveSchedule"
disabled={!hasChanges || status === Status.LOADING}
onClick={() => makeRequest({ connectorId: index.connector.id, scheduling })}
>

View file

@ -39,6 +39,7 @@ export const NativeConnectorAdvancedConfiguration: React.FC = () => {
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonTo
data-telemetry-id="entSearchContent-connector-configuration-setScheduleAndSync"
to={`${generateEncodedPath(SEARCH_INDEX_TAB_PATH, {
indexName,
tabId: SearchIndexTabId.SCHEDULING,

View file

@ -45,6 +45,7 @@ export const ConnectorCheckable: React.FC<ConnectorCheckableProps> = ({
{...props}
id={`checkableCard-${serviceType}`}
className="connectorCheckable"
data-telemetry-id={`entSearchContent-connector-selectConnector-${serviceType}-select`}
label={
<EuiFlexGroup alignItems="center" gutterSize="s">
{icon && (

View file

@ -136,7 +136,13 @@ export const SelectConnector: React.FC = () => {
))}
</EuiFlexGroup>
<EuiSpacer />
<EuiButton fill color="primary" type="submit" disabled={selectedNativeConnector === null}>
<EuiButton
data-telemetry-id="entSearchContent-connector-selectConnector-selectAndConfigure"
fill
color="primary"
type="submit"
disabled={selectedNativeConnector === null}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.selectConnector.selectAndConfigureButtonLabel',
{

View file

@ -96,6 +96,7 @@ export const AutomaticCrawlScheduler: React.FC = () => {
>
<EuiFormRow display="rowCompressed">
<EuiSwitch
data-telemetry-id="entSearchContent-crawler-scheduleCrawl-crawlAutomatically"
autoFocus
checked={crawlAutomatically}
label={
@ -126,6 +127,7 @@ export const AutomaticCrawlScheduler: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFieldNumber
data-telemetry-id="entSearchContent-crawler-scheduleCrawl-crawlAutomatically-scheduleFrequency"
aria-label={i18n.translate(
'xpack.enterpriseSearch.crawler.automaticCrawlSchedule.scheduleFrequencyLabel',
{
@ -143,6 +145,7 @@ export const AutomaticCrawlScheduler: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSelect
data-telemetry-id="entSearchContent-crawler-scheduleCrawl-crawlAutomatically-scheduleUnits"
aria-label={i18n.translate(
'xpack.enterpriseSearch.crawler.automaticCrawlSchedule.scheduleUnitsLabel',
{
@ -188,7 +191,13 @@ export const AutomaticCrawlScheduler: React.FC = () => {
</EuiText>
<EuiSpacer />
<EuiFormRow display="rowCompressed">
<EuiButton form={formId} type="submit" isLoading={isSubmitting} fill>
<EuiButton
data-telemetry-id="entSearchContent-crawler-scheduleCrawl-crawlAutomatically-save"
form={formId}
type="submit"
isLoading={isSubmitting}
fill
>
{SAVE_BUTTON_LABEL}
</EuiButton>
</EuiFormRow>

View file

@ -84,10 +84,16 @@ export const CrawlCustomSettingsFlyout: React.FC = () => {
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={hideFlyout}>{CANCEL_BUTTON_LABEL}</EuiButtonEmpty>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-cancelStartCrawl"
onClick={hideFlyout}
>
{CANCEL_BUTTON_LABEL}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-startCrawl"
fill
onClick={startCustomCrawl}
disabled={isDataLoading || selectedDomainUrls.length === 0}

View file

@ -39,6 +39,7 @@ export const CrawlCustomSettingsFlyoutCrawlDepthPanel: React.FC = () => {
)}
>
<EuiFieldNumber
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-maxCrawlDepth"
min={1}
value={maxCrawlDepth}
onChange={(e: ChangeEvent<HTMLInputElement>) =>

View file

@ -50,11 +50,13 @@ describe('CrawlCustomSettingsFlyoutDomainsPanel', () => {
it('allows the user to select domains', () => {
const domainAccordionWrapper = wrapper.find(EuiAccordion);
expect(domainAccordionWrapper.find(SimplifiedSelectable).props()).toEqual({
options: ['https://www.elastic.co', 'https://www.swiftype.com'],
selectedOptions: ['https://www.elastic.co'],
onChange: MOCK_ACTIONS.onSelectDomainUrls,
});
expect(domainAccordionWrapper.find(SimplifiedSelectable).props()).toEqual(
expect.objectContaining({
options: ['https://www.elastic.co', 'https://www.swiftype.com'],
selectedOptions: ['https://www.elastic.co'],
onChange: MOCK_ACTIONS.onSelectDomainUrls,
})
);
});
it('indicates how many domains are selected', () => {

View file

@ -74,6 +74,7 @@ export const CrawlCustomSettingsFlyoutDomainsPanel: React.FC = () => {
}
>
<SimplifiedSelectable
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-selectDomainUrls"
options={domainUrls}
selectedOptions={selectedDomainUrls}
onChange={onSelectDomainUrls}

View file

@ -76,11 +76,13 @@ describe('CrawlCustomSettingsFlyoutSeedUrlsPanel', () => {
});
it('allows the user to select sitemap urls', () => {
expect(sitemapTab.find(SimplifiedSelectable).props()).toEqual({
options: MOCK_VALUES.sitemapUrls,
selectedOptions: MOCK_VALUES.selectedSitemapUrls,
onChange: MOCK_ACTIONS.onSelectSitemapUrls,
});
expect(sitemapTab.find(SimplifiedSelectable).props()).toEqual(
expect.objectContaining({
options: MOCK_VALUES.sitemapUrls,
selectedOptions: MOCK_VALUES.selectedSitemapUrls,
onChange: MOCK_ACTIONS.onSelectSitemapUrls,
})
);
});
it('allows the user to toggle whether to include robots.txt sitemaps', () => {
@ -111,11 +113,13 @@ describe('CrawlCustomSettingsFlyoutSeedUrlsPanel', () => {
});
it('allows the user to select entry point urls', () => {
expect(entryPointsTab.find(SimplifiedSelectable).props()).toEqual({
options: MOCK_VALUES.entryPointUrls,
selectedOptions: MOCK_VALUES.selectedEntryPointUrls,
onChange: MOCK_ACTIONS.onSelectEntryPointUrls,
});
expect(entryPointsTab.find(SimplifiedSelectable).props()).toEqual(
expect.objectContaining({
options: MOCK_VALUES.entryPointUrls,
selectedOptions: MOCK_VALUES.selectedEntryPointUrls,
onChange: MOCK_ACTIONS.onSelectEntryPointUrls,
})
);
});
it('allows the user to add custom entry point urls', () => {

View file

@ -113,6 +113,7 @@ export const CrawlCustomSettingsFlyoutSeedUrlsPanel: React.FC = () => {
<EuiSpacer size="s" />
<EuiPanel color="subdued" borderRadius="none" hasShadow={false} paddingSize="s">
<EuiCheckbox
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-includeRobotsSitemaps"
id={useGeneratedHtmlId({ prefix: 'includeRobotsCheckbox' })}
label={
<FormattedMessage
@ -128,6 +129,7 @@ export const CrawlCustomSettingsFlyoutSeedUrlsPanel: React.FC = () => {
/>
</EuiPanel>
<SimplifiedSelectable
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-selectDomain"
options={sitemapUrls}
selectedOptions={selectedSitemapUrls}
onChange={onSelectSitemapUrls}
@ -144,6 +146,7 @@ export const CrawlCustomSettingsFlyoutSeedUrlsPanel: React.FC = () => {
/>
<EuiHorizontalRule />
<UrlComboBox
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-customSitemapUrls"
label={i18n.translate(
'xpack.enterpriseSearch.crawler.crawlCustomSettingsFlyout.customSitemapUrlsTextboxLabel',
{
@ -168,6 +171,7 @@ export const CrawlCustomSettingsFlyoutSeedUrlsPanel: React.FC = () => {
<>
<EuiSpacer size="s" />
<SimplifiedSelectable
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-selectDomain"
options={entryPointUrls}
selectedOptions={selectedEntryPointUrls}
onChange={onSelectEntryPointUrls}
@ -184,6 +188,7 @@ export const CrawlCustomSettingsFlyoutSeedUrlsPanel: React.FC = () => {
/>
<EuiHorizontalRule />
<UrlComboBox
data-telemetry-id="entSearchContent-crawler-customCrawlSettings-customEntryPointUrls"
label={i18n.translate(
'xpack.enterpriseSearch.crawler.crawlCustomSettingsFlyout.customEntryPointUrlsTextboxLabel',
{

View file

@ -74,8 +74,19 @@ export const DeleteDomainModal: React.FC = () => {
</EuiText>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={hideModal}>{CANCEL_BUTTON_LABEL}</EuiButtonEmpty>
<EuiButton onClick={deleteDomain} isLoading={isLoading} color="danger" fill>
<EuiButtonEmpty
data-telemetry-id="entSearchContent-crawler-domainManagement-deleteDomain-cancel"
onClick={hideModal}
>
{CANCEL_BUTTON_LABEL}
</EuiButtonEmpty>
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainManagement-deleteDomain-delete"
onClick={deleteDomain}
isLoading={isLoading}
color="danger"
fill
>
{i18n.translate(
'xpack.enterpriseSearch.crawler.deleteDomainModal.deleteDomainButtonLabel',
{

View file

@ -43,7 +43,13 @@ export const DomainsPanel: React.FC = () => {
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={openFlyout} size="s" color="success" iconType="plusInCircle">
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainManagement-addDomain-addDomain"
onClick={openFlyout}
size="s"
color="success"
iconType="plusInCircle"
>
{i18n.translate('xpack.enterpriseSearch.crawler.addDomainFlyout.openButtonLabel', {
defaultMessage: 'Add domain',
})}

View file

@ -55,7 +55,11 @@ export const EmptyStatePanel: React.FC = () => {
<EuiSpacer />
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiButton onClick={openFlyout} fill>
<EuiButton
data-telemetry-id="entSearchContent-crawler-domainManagement-noDomains-addFirstDomain"
onClick={openFlyout}
fill
>
{i18n.translate(
'xpack.enterpriseSearch.crawler.domainManagement.emptyState.addDomainButtonLabel',
{

View file

@ -26,9 +26,11 @@ import { DocumentsLogic, DEFAULT_PAGINATION } from './documents_logic';
import { IndexNameLogic } from './index_name_logic';
import './documents.scss';
import { IndexViewLogic } from './index_view_logic';
export const SearchIndexDocuments: React.FC = () => {
const { indexName } = useValues(IndexNameLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { simplifiedMapping } = useValues(DocumentsLogic);
const { makeRequest, makeMappingRequest, setSearchQuery } = useActions(DocumentsLogic);
@ -58,6 +60,7 @@ export const SearchIndexDocuments: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem>
<EuiFieldSearch
data-telemetry-id={`entSearchContent-${ingestionMethod}-documents-searchDocuments`}
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.searchIndex.documents.searchField.placeholder',
{

View file

@ -27,7 +27,7 @@ import { OverviewLogic } from './overview.logic';
export const GenerateApiKeyPanel: React.FC = () => {
const { apiKey, isGenerateModalOpen } = useValues(OverviewLogic);
const { indexName } = useValues(IndexViewLogic);
const { indexName, ingestionMethod } = useValues(IndexViewLogic);
const { closeGenerateModal } = useActions(OverviewLogic);
const { defaultPipeline } = useValues(SettingsLogic);
@ -58,6 +58,7 @@ export const GenerateApiKeyPanel: React.FC = () => {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSwitch
data-telemetry-id={`entSearchContent-${ingestionMethod}-overview-generateApiKey-optimizedRequest`}
onChange={(event) => setOptimizedRequest(event.target.checked)}
label={i18n.translate(
'xpack.enterpriseSearch.content.overview.optimizedRequest.label',

View file

@ -15,9 +15,10 @@ import { EuiButtonEmptyTo } from '../../../../shared/react_router_helpers';
export const CustomPipelinePanel: React.FC<{
indexName: string;
ingestionMethod: string;
pipelineSuffix: string;
processorsCount: number;
}> = ({ indexName, pipelineSuffix, processorsCount }) => {
}> = ({ indexName, ingestionMethod, pipelineSuffix, processorsCount }) => {
return (
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexItem>
@ -29,6 +30,7 @@ export const CustomPipelinePanel: React.FC<{
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmptyTo
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-customPipeline-editPipeline`}
to={`/app/management/ingest/ingest_pipelines/?pipeline=${indexName}@${pipelineSuffix}`}
shouldNotCreateHref
>

View file

@ -32,12 +32,15 @@ import { HttpLogic } from '../../../../shared/http';
import { ML_MANAGE_TRAINED_MODELS_PATH } from '../../../routes';
import { IndexNameLogic } from '../index_name_logic';
import { IndexViewLogic } from '../index_view_logic';
import { TrainedModelHealth } from './ml_model_health';
import { PipelinesLogic } from './pipelines_logic';
export const InferencePipelineCard: React.FC<InferencePipeline> = (pipeline) => {
const { http } = useValues(HttpLogic);
const { indexName } = useValues(IndexNameLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const [isPopOverOpen, setIsPopOverOpen] = useState(false);
const [showConfirmDelete, setShowConfirmDelete] = useState(false);
const { deleteMlPipeline } = useActions(PipelinesLogic);
@ -85,6 +88,7 @@ export const InferencePipelineCard: React.FC<InferencePipeline> = (pipeline) =>
<EuiFlexItem>
<div>
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-inferencePipeline-stackManagement`}
size="s"
flush="both"
iconType="eye"
@ -103,6 +107,7 @@ export const InferencePipelineCard: React.FC<InferencePipeline> = (pipeline) =>
<EuiFlexItem>
<div>
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-inferencePipeline-deletePipeline`}
size="s"
flush="both"
iconType="trash"
@ -138,6 +143,7 @@ export const InferencePipelineCard: React.FC<InferencePipeline> = (pipeline) =>
)}
>
<EuiButtonIcon
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-inferencePipeline-fixIssueInTrainedModels`}
href={http.basePath.prepend(ML_MANAGE_TRAINED_MODELS_PATH)}
display="base"
size="xs"

View file

@ -40,6 +40,7 @@ interface IngestPipelineModalProps {
createCustomPipelines: () => void;
displayOnly: boolean;
indexName: string;
ingestionMethod: string;
isGated: boolean;
isLoading: boolean;
pipeline: IngestPipelineParams;
@ -53,6 +54,7 @@ export const IngestPipelineModal: React.FC<IngestPipelineModalProps> = ({
createCustomPipelines,
displayOnly,
indexName,
ingestionMethod,
isGated,
isLoading,
pipeline,
@ -160,7 +162,11 @@ export const IngestPipelineModal: React.FC<IngestPipelineModalProps> = ({
</EuiText>
</EuiFormRow>
<EuiFormRow fullWidth>
<PipelineSettingsForm pipeline={pipeline} setPipeline={setPipeline} />
<PipelineSettingsForm
ingestionMethod={ingestionMethod}
pipeline={pipeline}
setPipeline={setPipeline}
/>
</EuiFormRow>
</EuiForm>
<EuiSpacer />
@ -206,6 +212,7 @@ export const IngestPipelineModal: React.FC<IngestPipelineModalProps> = ({
<EuiFlexItem grow={false}>
<EuiFlexGroup justifyContent="flexStart">
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-copyAndCustomize`}
disabled={isGated}
iconType={isGated ? 'lock' : undefined}
onClick={createCustomPipelines}

View file

@ -35,7 +35,7 @@ import { IngestPipelineModal } from './ingest_pipeline_modal';
import { PipelinesLogic } from './pipelines_logic';
export const IngestPipelinesCard: React.FC = () => {
const { indexName } = useValues(IndexViewLogic);
const { indexName, ingestionMethod } = useValues(IndexViewLogic);
const { canSetPipeline, index, pipelineName, pipelineState, showModal } =
useValues(PipelinesLogic);
@ -60,6 +60,7 @@ export const IngestPipelinesCard: React.FC = () => {
createCustomPipelines={() => createCustomPipeline({ indexName })}
displayOnly={!canSetPipeline}
indexName={indexName}
ingestionMethod={ingestionMethod}
isGated={isGated}
isLoading={false}
pipeline={{ ...pipelineState, name: pipelineName }}
@ -72,6 +73,7 @@ export const IngestPipelinesCard: React.FC = () => {
<EuiPanel color="primary">
<CustomPipelinePanel
indexName={indexName}
ingestionMethod={ingestionMethod}
pipelineSuffix="custom"
processorsCount={customPipeline.processors?.length ?? 0}
/>
@ -89,7 +91,10 @@ export const IngestPipelinesCard: React.FC = () => {
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={openModal}>
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-settings`}
onClick={openModal}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.ingestPipelinesCard.settings.label',
{ defaultMessage: 'Settings' }
@ -103,6 +108,7 @@ export const IngestPipelinesCard: React.FC = () => {
{isApiIndex(index) && (
<EuiFlexItem>
<EuiAccordion
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-viewCurlRequest`}
buttonContent={i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.ingestPipelinesCard.accordion.label',
{ defaultMessage: 'View sample cURL request' }

View file

@ -13,6 +13,7 @@ import { EuiButton, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { KibanaLogic } from '../../../../../shared/kibana/kibana_logic';
import { IndexViewLogic } from '../../index_view_logic';
import { PipelinesLogic } from '../pipelines_logic';
export interface AddMLInferencePipelineButtonProps {
@ -22,6 +23,7 @@ export const AddMLInferencePipelineButton: React.FC<AddMLInferencePipelineButton
onClick,
}) => {
const { capabilities } = useValues(KibanaLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { canUseMlInferencePipeline, hasIndexIngestionPipeline } = useValues(PipelinesLogic);
const hasMLPermissions = capabilities?.ml?.canAccessML ?? false;
if (!hasMLPermissions) {
@ -32,7 +34,7 @@ export const AddMLInferencePipelineButton: React.FC<AddMLInferencePipelineButton
{ defaultMessage: 'You do not have permission to Machine Learning on this cluster.' }
)}
>
<AddButton disabled />
<AddButton ingestionMethod={ingestionMethod} disabled />
</EuiToolTip>
);
}
@ -47,7 +49,7 @@ export const AddMLInferencePipelineButton: React.FC<AddMLInferencePipelineButton
}
)}
>
<AddButton disabled />
<AddButton ingestionMethod={ingestionMethod} disabled />
</EuiToolTip>
);
}
@ -62,18 +64,25 @@ export const AddMLInferencePipelineButton: React.FC<AddMLInferencePipelineButton
}
)}
>
<AddButton disabled />
<AddButton ingestionMethod={ingestionMethod} disabled />
</EuiToolTip>
);
}
return <AddButton onClick={onClick} />;
return <AddButton ingestionMethod={ingestionMethod} onClick={onClick} />;
};
const AddButton: React.FC<{ disabled?: boolean; onClick?: () => void }> = ({
disabled,
onClick,
}) => (
<EuiButton color="success" disabled={disabled} iconType="plusInCircle" onClick={onClick}>
const AddButton: React.FC<{
disabled?: boolean;
ingestionMethod: string;
onClick?: () => void;
}> = ({ disabled, ingestionMethod, onClick }) => (
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-addInferencePipeline`}
color="success"
disabled={disabled}
iconType="plusInCircle"
onClick={onClick}
>
{i18n.translate('xpack.enterpriseSearch.content.indices.pipelines.mlInference.addButtonLabel', {
defaultMessage: 'Add inference pipeline',
})}

View file

@ -35,6 +35,8 @@ import {
} from '../../../../../shared/constants';
import { IndexNameLogic } from '../../index_name_logic';
import { IndexViewLogic } from '../../index_view_logic';
import { ConfigurePipeline } from './configure_pipeline';
import { AddInferencePipelineSteps, MLInferenceLogic } from './ml_inference_logic';
import { NoModelsPanel } from './no_models';
@ -74,6 +76,7 @@ export const AddMLInferencePipelineModal: React.FC<AddMLInferencePipelineModalPr
};
const AddProcessorContent: React.FC<AddMLInferencePipelineModalProps> = ({ onClose }) => {
const { ingestionMethod } = useValues(IndexViewLogic);
const {
createErrors,
supportedMLModels,
@ -115,7 +118,7 @@ const AddProcessorContent: React.FC<AddMLInferencePipelineModalProps> = ({ onClo
{step === AddInferencePipelineSteps.Test && <TestPipeline />}
{step === AddInferencePipelineSteps.Review && <ReviewPipeline />}
</EuiModalBody>
<ModalFooter onClose={onClose} />
<ModalFooter ingestionMethod={ingestionMethod} onClose={onClose} />
</>
);
};
@ -172,7 +175,10 @@ const ModalSteps: React.FC = () => {
return <EuiStepsHorizontal steps={navSteps} />;
};
const ModalFooter: React.FC<AddMLInferencePipelineModalProps> = ({ onClose }) => {
const ModalFooter: React.FC<AddMLInferencePipelineModalProps & { ingestionMethod: string }> = ({
ingestionMethod,
onClose,
}) => {
const { addInferencePipelineModal: modal, isPipelineDataValid } = useValues(MLInferenceLogic);
const { createPipeline, setAddInferencePipelineStep } = useActions(MLInferenceLogic);
@ -206,11 +212,17 @@ const ModalFooter: React.FC<AddMLInferencePipelineModalProps> = ({ onClose }) =>
</EuiFlexItem>
<EuiFlexItem />
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={onClose}>{CANCEL_BUTTON_LABEL}</EuiButtonEmpty>
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-addMlInference-cancel`}
onClick={onClose}
>
{CANCEL_BUTTON_LABEL}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{nextStep !== undefined ? (
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-addMlInference-continue`}
iconType="arrowRight"
iconSide="right"
onClick={() => setAddInferencePipelineStep(nextStep as AddInferencePipelineSteps)}
@ -219,7 +231,12 @@ const ModalFooter: React.FC<AddMLInferencePipelineModalProps> = ({ onClose }) =>
{CONTINUE_BUTTON_LABEL}
</EuiButton>
) : (
<EuiButton color="success" disabled={!isPipelineDataValid} onClick={createPipeline}>
<EuiButton
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-addMlInference-create`}
color="success"
disabled={!isPipelineDataValid}
onClick={createPipeline}
>
{i18n.translate(
'xpack.enterpriseSearch.content.indices.transforms.addInferencePipelineModal.footer.create',
{

View file

@ -26,6 +26,8 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { docLinks } from '../../../../../shared/doc_links';
import { IndexViewLogic } from '../../index_view_logic';
import { MLInferenceLogic } from './ml_inference_logic';
const NoSourceFieldsError: React.FC = () => (
@ -53,6 +55,7 @@ export const ConfigurePipeline: React.FC = () => {
sourceFields,
} = useValues(MLInferenceLogic);
const { setInferencePipelineConfiguration } = useActions(MLInferenceLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { destinationField, modelID, pipelineName, sourceField } = configuration;
const models = supportedMLModels ?? [];
@ -104,6 +107,7 @@ export const ConfigurePipeline: React.FC = () => {
isInvalid={nameError}
>
<EuiFieldText
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureInferencePipeline-uniqueName`}
fullWidth
placeholder={i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.namePlaceholder',
@ -131,6 +135,7 @@ export const ConfigurePipeline: React.FC = () => {
fullWidth
>
<EuiSelect
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureInferencePipeline-selectTrainedModel`}
fullWidth
value={modelID}
options={[
@ -166,6 +171,7 @@ export const ConfigurePipeline: React.FC = () => {
isInvalid={emptySourceFields}
>
<EuiSelect
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureInferencePipeline-selectSchemaField`}
value={sourceField}
options={[
{
@ -215,6 +221,7 @@ export const ConfigurePipeline: React.FC = () => {
isInvalid={formErrors.destinationField !== undefined}
>
<EuiFieldText
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-configureInferencePipeline-destionationField`}
placeholder="custom_field_name"
value={destinationField}
onChange={(e) =>

View file

@ -14,11 +14,13 @@ import { IngestPipelineParams } from '../../../../../../common/types/connectors'
import { SettingsCheckableCard } from '../../shared/settings_checkable_card/settings_checkable_card';
interface PipelineSettingsFormProps {
ingestionMethod: string;
pipeline: IngestPipelineParams;
setPipeline: (pipeline: IngestPipelineParams) => void;
}
export const PipelineSettingsForm: React.FC<PipelineSettingsFormProps> = ({
ingestionMethod,
setPipeline,
pipeline,
}) => {
@ -31,6 +33,7 @@ export const PipelineSettingsForm: React.FC<PipelineSettingsFormProps> = ({
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
<SettingsCheckableCard
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-extractBinaryContent`}
description={i18n.translate(
'xpack.enterpriseSearch.content.index.pipelines.settings.extractBinaryDescription',
{
@ -55,6 +58,7 @@ export const PipelineSettingsForm: React.FC<PipelineSettingsFormProps> = ({
</EuiFlexItem>
<EuiFlexItem>
<SettingsCheckableCard
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-reduceWhitespace`}
id="ingestPipelineReduceWhitespace"
checked={reduceWhitespace}
description={i18n.translate(
@ -76,6 +80,7 @@ export const PipelineSettingsForm: React.FC<PipelineSettingsFormProps> = ({
</EuiFlexItem>
<EuiFlexItem>
<SettingsCheckableCard
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-runMlInference`}
id="ingestPipelineRunMlInference"
checked={runMLInference}
description={i18n.translate(

View file

@ -28,11 +28,14 @@ import { docLinks } from '../../../../shared/doc_links';
import { HttpLogic } from '../../../../shared/http';
import { isManagedPipeline } from '../../../../shared/pipelines/is_managed';
import { IndexViewLogic } from '../index_view_logic';
import { PipelineJSONBadges } from './pipeline_json_badges';
import { IndexPipelinesConfigurationsLogic } from './pipelines_json_configurations_logic';
export const PipelinesJSONConfigurations: React.FC = () => {
const { http } = useValues(HttpLogic);
const { ingestionMethod } = useValues(IndexViewLogic);
const { pipelineNames, selectedPipeline, selectedPipelineId, selectedPipelineJSON } = useValues(
IndexPipelinesConfigurationsLogic
);
@ -79,6 +82,7 @@ export const PipelinesJSONConfigurations: React.FC = () => {
)}
>
<EuiSelect
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-selectPipeline`}
fullWidth
options={pipelineNames.map((name) => ({ text: name, value: name }))}
value={selectedPipelineId}
@ -95,6 +99,7 @@ export const PipelinesJSONConfigurations: React.FC = () => {
<EuiFlexItem grow={false}>
{isManagedPipeline(selectedPipeline) ? (
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-viewInStackManagement`}
size="s"
flush="both"
iconType="eye"
@ -112,6 +117,7 @@ export const PipelinesJSONConfigurations: React.FC = () => {
</EuiButtonEmpty>
) : (
<EuiButtonEmpty
data-telemetry-id={`entSearchContent-${ingestionMethod}-pipelines-ingestPipelines-editInStackManagement`}
size="s"
flush="both"
iconType="pencil"

View file

@ -30,8 +30,6 @@ import { EnterpriseSearchContentPageTemplate } from '../layout/page_template';
import { baseBreadcrumbs } from '../search_indices';
import { getHeaderActions } from './components/header_actions/header_actions';
import { IndexCreatedCallout } from './components/index_created_callout/callout';
import { IndexCreatedCalloutLogic } from './components/index_created_callout/callout_logic';
import { ConnectorConfiguration } from './connector/connector_configuration';
import { ConnectorSchedulingComponent } from './connector/connector_scheduling';
import { AutomaticCrawlScheduler } from './crawler/automatic_crawl_scheduler/automatic_crawl_scheduler';
@ -58,7 +56,6 @@ export enum SearchIndexTabId {
export const SearchIndex: React.FC = () => {
const { data: indexData, status: indexApiStatus } = useValues(FetchIndexApiLogic);
const { isCalloutVisible } = useValues(IndexCreatedCalloutLogic);
const { tabId = SearchIndexTabId.OVERVIEW } = useParams<{
tabId?: string;
}>();
@ -177,7 +174,6 @@ export const SearchIndex: React.FC = () => {
}}
>
<>
{isCalloutVisible && <IndexCreatedCallout indexName={indexName} />}
{indexName === indexData?.name && (
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={onTabClick} />
)}

View file

@ -12,9 +12,9 @@ export interface Crawler {
}
export const enum IngestionMethod {
CONNECTOR,
CRAWLER,
API,
CONNECTOR = 'connector',
CRAWLER = 'crawler',
API = 'api',
}
export const enum IngestionStatus {

View file

@ -11360,11 +11360,6 @@
"xpack.enterpriseSearch.content.callout.dismissButton": "Rejeter",
"xpack.enterpriseSearch.content.callout.title": "Présentation des index Elasticsearch dans Enterprise Search",
"xpack.enterpriseSearch.content.description": "Enterprise Search offre un certain nombre de moyens de rendre vos données facilement interrogeables. Vous pouvez choisir entre le robot d'indexation, les indices Elasticsearch, l'API, les téléchargements directs ou les connecteurs tiers.",
"xpack.enterpriseSearch.content.index.createAppSearchEngine.button": "Créer un moteur App Search",
"xpack.enterpriseSearch.content.index.dismiss.button": "Rejeter",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.info": "Vous pouvez utiliser des moteurs App Search pour créer une expérience de recherche pour votre nouvel index Elasticsearch.",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.title": "Index Elasticsearch créé avec succès",
"xpack.enterpriseSearch.content.index.readDocumentation.link": "Lire la documentation",
"xpack.enterpriseSearch.content.index.searchEngines.createEngine": "Créer un moteur App Search",
"xpack.enterpriseSearch.content.index.searchEngines.label": "Moteurs de recherche",
"xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "Afficher les moteurs App Search",

View file

@ -11346,11 +11346,6 @@
"xpack.enterpriseSearch.content.callout.dismissButton": "閉じる",
"xpack.enterpriseSearch.content.callout.title": "エンタープライズ サーチでのElasticsearchインデックスの概要",
"xpack.enterpriseSearch.content.description": "エンタープライズ サーチでは、さまざまな方法で簡単にデータを検索可能にできます。Webクローラー、Elasticsearchインデックス、API、直接アップロード、サードパーティコネクターから選択します。",
"xpack.enterpriseSearch.content.index.createAppSearchEngine.button": "App Searchエンジンの作成",
"xpack.enterpriseSearch.content.index.dismiss.button": "閉じる",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.info": "App Searchエンジンを使用して、新しいElasticsearchインデックスの検索エクスペリエンスを構築できます。",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.title": "Elasticsearchインデックスは正常に作成されました",
"xpack.enterpriseSearch.content.index.readDocumentation.link": "ドキュメントを読む",
"xpack.enterpriseSearch.content.index.searchEngines.createEngine": "新しいApp Searchエンジンの作成",
"xpack.enterpriseSearch.content.index.searchEngines.label": "検索エンジン",
"xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "App Searchエンジンを表示",

View file

@ -11365,11 +11365,6 @@
"xpack.enterpriseSearch.content.callout.dismissButton": "关闭",
"xpack.enterpriseSearch.content.callout.title": "在 Enterprise Search 中引入 Elasticsearch 索引",
"xpack.enterpriseSearch.content.description": "Enterprise Search 提供了各种方法以便您轻松搜索数据。从网络爬虫、Elasticsearch 索引、API、直接上传或第三方连接器中选择。",
"xpack.enterpriseSearch.content.index.createAppSearchEngine.button": "创建 App Search 引擎",
"xpack.enterpriseSearch.content.index.dismiss.button": "关闭",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.info": "您可以使用 App Search 引擎为您的新 Elasticsearch 索引构建搜索体验。",
"xpack.enterpriseSearch.content.index.indexCreatedCallout.title": "已成功创建 Elasticsearch 索引",
"xpack.enterpriseSearch.content.index.readDocumentation.link": "阅读文档",
"xpack.enterpriseSearch.content.index.searchEngines.createEngine": "创建新的 App Search 引擎",
"xpack.enterpriseSearch.content.index.searchEngines.label": "搜索引擎",
"xpack.enterpriseSearch.content.index.searchEngines.viewEngines": "查看 App Search 引擎",