diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx index 53cbf579a940f..a905242c90fec 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/components/generated_config_fields.tsx @@ -97,6 +97,9 @@ export const GeneratedConfigFields: React.FC = ({ setIsModalVisible(false); }; + const showApiKeyInfoForSelfManagedConnector = !connector.is_native; + const showApiKeyBanner = showApiKeyInfoForSelfManagedConnector && apiKey?.encoded; + return ( <> {isModalVisible && } @@ -193,96 +196,99 @@ export const GeneratedConfigFields: React.FC = ({ )} - - + {showApiKeyInfoForSelfManagedConnector && ( + <> + + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.connectorDeployment.apiKeyCreatedFlexItemLabel', + { defaultMessage: 'API key created' } + )} + {apiKey?.encoded && ` *`} + + + - + + {apiKey?.name} + - {i18n.translate( - 'xpack.enterpriseSearch.connectorDeployment.apiKeyCreatedFlexItemLabel', - { defaultMessage: 'API key created' } - )} - {apiKey?.encoded && ` *`} - - - - - - {apiKey?.name} - - - - - {apiKey?.encoded ? ( - - - {(copy) => ( - - - {apiKey?.encoded} - - {generateApiKey && ( - - - - )} - - + {apiKey?.encoded ? ( + + + {(copy) => ( + + + {apiKey?.encoded} + + {generateApiKey && ( + + + )} - /> - - - )} - - - ) : ( - generateApiKey && ( - - - - ) - )} - - + + + + + )} + + + ) : ( + generateApiKey && ( + + + + ) + )} + + + + )} - - {apiKey?.encoded && ( + {showApiKeyBanner && ( <> [selectors.hasAdvancedFilteringFeature, selectors.hasBasicFilteringFeature], (advancedFeature: boolean, basicFeature: boolean) => advancedFeature || basicFeature, ], + hasIncrementalSyncFeature: [ () => [selectors.connector], (connector?: Connector) => hasIncrementalSyncFeature(connector), @@ -231,6 +237,14 @@ export const ConnectorViewLogic = kea [selectors.connector], + (connector: Connector) => { + if (!connector || !connector.is_native) return false; + + return !hasConnectorBeenSeenRecently(connector); + }, + ], pipelineData: [ () => [selectors.connector], (connector: Connector | undefined) => connector?.pipeline ?? undefined, diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx index 3fdd3d379eacb..9024f67858d33 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx @@ -9,7 +9,17 @@ import React from 'react'; import { useActions, useValues } from 'kea'; -import { EuiButton, EuiCallOut, EuiCode, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiButton, + EuiCallOut, + EuiCode, + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiLoadingSpinner, + EuiSpacer, + EuiText, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -38,7 +48,7 @@ import { ConnectorViewLogic } from './connector_view_logic'; export const ConnectorDetailOverview: React.FC = () => { const { indexData } = useValues(IndexViewLogic); - const { connector, error } = useValues(ConnectorViewLogic); + const { connector, error, isWaitingOnAgentlessDeployment } = useValues(ConnectorViewLogic); const { isCloud } = useValues(KibanaLogic); const { showModal } = useActions(ConvertConnectorLogic); const { isModalVisible } = useValues(ConvertConnectorLogic); @@ -73,6 +83,39 @@ export const ConnectorDetailOverview: React.FC = () => { ) } + {isWaitingOnAgentlessDeployment && ( + <> + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.connectors.overview.agentlessDeploymentNotReadyCallOut.title', + { + defaultMessage: 'Provisioning infrastructure', + } + )} + + + } + > + + + {i18n.translate( + 'xpack.enterpriseSearch.content.connectors.overview.agentlessDeploymentNotReadyCallOut.description', + { + defaultMessage: 'Setting up the agentless infrastructure to run the connector.', + } + )} + + + + + )} {error && ( <> = ({ selfManaged }) => { +export const ChooseConnector: React.FC = ({ + selfManaged, + disabled, +}) => { const { euiTheme } = useEuiTheme(); const [selectedOption, setSelectedOption] = useState>>( [] @@ -142,6 +146,7 @@ export const ChooseConnector: React.FC = ({ self return ( = ({ title, setCurrentStep }) => { - const { connector } = useValues(ConnectorViewLogic); + const { connector, isWaitingOnAgentlessDeployment } = useValues(ConnectorViewLogic); const { updateConnectorConfiguration } = useActions(ConnectorViewLogic); const { setFormDirty } = useActions(NewConnectorLogic); const { overlays } = useKibana().services; @@ -46,10 +48,12 @@ export const ConfigurationStep: React.FC = ({ title, set const { status } = useValues(ConnectorConfigurationApiLogic); const isSyncing = false; - const isNextStepEnabled = + const isConnectorConfigured = connector?.status === ConnectorStatus.CONNECTED || connector?.status === ConnectorStatus.CONFIGURED; + const isNextStepEnabled = !isWaitingOnAgentlessDeployment && isConnectorConfigured; + useEffect(() => { setTimeout(() => { window.scrollTo({ @@ -64,6 +68,37 @@ export const ConfigurationStep: React.FC = ({ title, set return ( <> + {isWaitingOnAgentlessDeployment && ( + + + + + + {i18n.translate( + 'xpack.enterpriseSearch.createConnector.configurationStep.agentlessDeploymentNotReadyCallOut.title', + { + defaultMessage: 'Provisioning infrastructure', + } + )} + + + } + > + + + {i18n.translate( + 'xpack.enterpriseSearch.createConnector.configurationStep.agentlessDeploymentNotReadyCallOut.description', + { + defaultMessage: + 'Setting up the agentless infrastructure to run the connector. This process may take up to one minute.', + } + )} + + + )} @@ -114,6 +149,7 @@ export const ConfigurationStep: React.FC = ({ title, set { if (isFormEditing) { diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx index 978a87fec1220..c0d7a2af0a682 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/create_connector/start_step.tsx @@ -107,7 +107,7 @@ export const StartStep: React.FC = ({ { defaultMessage: 'Connector' } )} > - + @@ -138,7 +138,7 @@ export const StartStep: React.FC = ({ = ({ } > diff --git a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts index c9e2bb0dd2b45..115601749f1c8 100644 --- a/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts +++ b/x-pack/solutions/search/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts @@ -26,6 +26,18 @@ export const getConnectorLastSeenError = (connector: Connector): string => { ); }; +// Determines if the connector has been seen recently. +// Note: The default heartbeat interval for the connector service is every 5 minutes. +// This is configured using the `service.heartbeat` key in: +// https://github.com/elastic/connectors/blob/main/connectors/config.py +export const hasConnectorBeenSeenRecently = ( + connector: Connector, + timeWindowMinutes: number = 10 +): boolean => + connector.last_seen + ? moment(connector.last_seen).isSameOrAfter(moment().subtract(timeWindowMinutes, 'minutes')) + : false; + const incompleteText = i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label', { defaultMessage: 'Incomplete' }