From 664c1a0a08003f82cb88c9b4a51f194fbb42cd84 Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Wed, 10 Jan 2024 13:20:53 +0100 Subject: [PATCH] Search use actual es endpoint in guides (#174518) ## Summary Use the actual configured Elasticsearch endpoint in the search plugin, outside of cloud where we still use the cloud configured endpoint. --- .../__mocks__/kea_logic/kibana_logic.mock.ts | 1 + .../elasticsearch_guide.tsx | 5 +- .../public/applications/index.tsx | 5 +- .../shared/api_key/api_key_panel.tsx | 5 +- .../getting_started/languages/constants.ts | 1 - .../shared/kibana/kibana_logic.ts | 5 ++ .../shared/layout/endpoints_header_action.tsx | 5 +- .../test_helpers/test_utils.test_helper.tsx | 3 + .../enterprise_search/public/plugin.ts | 22 ++++++- .../__mocks__/routerDependencies.mock.ts | 2 + .../server/lib/check_access.test.ts | 3 + .../lib/enterprise_search_config_api.test.ts | 3 + .../enterprise_search/server/plugin.ts | 34 +++++++++-- .../routes/enterprise_search/config_data.ts | 20 ++++++- .../server/services/global_config_service.ts | 57 +++++++++++++++++++ 15 files changed, 152 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/server/services/global_config_service.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts index 0c092b9d806b3..4388276117fc7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/kea_logic/kibana_logic.mock.ts @@ -34,6 +34,7 @@ export const mockKibanaValues = { }, config: { host: 'http://localhost:3002' }, data: dataPluginMock.createStartContract(), + esConfig: { elasticsearch_host: 'https://your_deployment_url' }, guidedOnboarding: {}, history: mockHistory, isCloud: false, diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx index dd82686a31405..f89547b00c3cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx @@ -13,22 +13,23 @@ import { EuiHorizontalRule, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LanguageDefinitionSnippetArguments } from '@kbn/search-api-panels'; -import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; import { FetchApiKeysAPILogic } from '../../../enterprise_search_overview/api/fetch_api_keys_logic'; import { CreateApiKeyFlyout } from '../../../shared/api_key/create_api_key_flyout'; import { useCloudDetails } from '../../../shared/cloud_details/cloud_details'; import { GettingStarted } from '../../../shared/getting_started/getting_started'; +import { KibanaLogic } from '../../../shared/kibana'; import { EnterpriseSearchElasticsearchPageTemplate } from '../layout'; export const ElasticsearchGuide = () => { const cloudContext = useCloudDetails(); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const { esConfig } = useValues(KibanaLogic); const codeArgs: LanguageDefinitionSnippetArguments = { apiKey: '', cloudId: cloudContext.cloudId, - url: cloudContext.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER, + url: esConfig.elasticsearch_host, }; const { makeRequest } = useActions(FetchApiKeysAPILogic); const { data } = useValues(FetchApiKeysAPILogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 55362dbfa8430..74c81d30a0825 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -22,7 +22,7 @@ import { Router } from '@kbn/shared-ux-router'; import { DEFAULT_PRODUCT_FEATURES } from '../../common/constants'; import { ClientConfigType, InitialAppData, ProductAccess } from '../../common/types'; -import { PluginsStart, ClientData } from '../plugin'; +import { PluginsStart, ClientData, ESConfig } from '../plugin'; import { externalUrl } from './shared/enterprise_search_url'; import { mountFlashMessagesLogic, Toasts } from './shared/flash_messages'; @@ -50,7 +50,7 @@ export const renderApp = ( params: AppMountParameters; plugins: PluginsStart; }, - { config, data }: { config: ClientConfigType; data: ClientData } + { config, data, esConfig }: { config: ClientConfigType; data: ClientData; esConfig: ESConfig } ) => { const { access, @@ -106,6 +106,7 @@ export const renderApp = ( charts, cloud, config, + esConfig, data: plugins.data, guidedOnboarding, history, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx index 5556b284d8d4a..c5d1034f488d8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx @@ -25,7 +25,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; import { FetchApiKeysAPILogic } from '../../enterprise_search_overview/api/fetch_api_keys_logic'; import { KibanaLogic } from '../kibana'; @@ -37,16 +36,16 @@ const COPIED_LABEL = i18n.translate('xpack.enterpriseSearch.overview.apiKey.copi }); export const ApiKeyPanel: React.FC = () => { - const { cloud, navigateToUrl } = useValues(KibanaLogic); + const { cloud, esConfig, navigateToUrl } = useValues(KibanaLogic); const { makeRequest } = useActions(FetchApiKeysAPILogic); const { data } = useValues(FetchApiKeysAPILogic); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const elasticsearchEndpoint = esConfig.elasticsearch_host; useEffect(() => makeRequest({}), []); const apiKeys = data?.api_keys || []; const cloudId = cloud?.cloudId; - const elasticsearchEndpoint = cloud?.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER; return ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts index b0b122fa01b5c..4e7036600eaa9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts @@ -6,5 +6,4 @@ */ export const API_KEY_PLACEHOLDER = 'your_api_key'; -export const ELASTICSEARCH_URL_PLACEHOLDER = 'https://your_deployment_url'; export const INDEX_NAME_PLACEHOLDER = 'index_name'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts index cf7e92dbd3475..201e91b439bc0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana/kibana_logic.ts @@ -19,13 +19,16 @@ import { IUiSettingsClient, } from '@kbn/core/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; + import { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { MlPluginStart } from '@kbn/ml-plugin/public'; +import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; import { AuthenticatedUser, SecurityPluginStart } from '@kbn/security-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { ClientConfigType, ProductAccess, ProductFeatures } from '../../../../common/types'; +import { ESConfig } from '../../../plugin'; import { HttpLogic } from '../http'; import { createHref, CreateHrefOptions } from '../react_router_helpers'; @@ -40,6 +43,7 @@ export interface KibanaLogicProps { cloud?: CloudSetup; config: ClientConfigType; data: DataPublicPluginStart; + esConfig: ESConfig; guidedOnboarding?: GuidedOnboardingPluginStart; history: ScopedHistory; isSidebarEnabled: boolean; @@ -75,6 +79,7 @@ export const KibanaLogic = kea>({ cloud: [props.cloud || {}, {}], config: [props.config || {}, {}], data: [props.data, {}], + esConfig: [props.esConfig || { elasticsearch_host: ELASTICSEARCH_URL_PLACEHOLDER }, {}], guidedOnboarding: [props.guidedOnboarding, {}], history: [props.history, {}], isSidebarEnabled: [props.isSidebarEnabled, {}], diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx index bbb27c342c992..35f1d28378c74 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx @@ -33,7 +33,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; import { FetchApiKeysAPILogic } from '../../enterprise_search_overview/api/fetch_api_keys_logic'; import { CreateApiKeyFlyout } from '../api_key/create_api_key_flyout'; @@ -43,7 +42,7 @@ import { EndpointIcon } from './endpoint_icon'; export const EndpointsHeaderAction: React.FC = ({ children }) => { const [isPopoverOpen, setPopoverOpen] = useState(false); - const { cloud, navigateToUrl } = useValues(KibanaLogic); + const { cloud, esConfig, navigateToUrl } = useValues(KibanaLogic); const { makeRequest } = useActions(FetchApiKeysAPILogic); const { data } = useValues(FetchApiKeysAPILogic); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); @@ -57,7 +56,7 @@ export const EndpointsHeaderAction: React.FC = ({ children }) => { const apiKeys = data?.api_keys || []; const cloudId = cloud?.cloudId; - const elasticsearchEndpoint = cloud?.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER; + const elasticsearchEndpoint = esConfig.elasticsearch_host; const button = ( setPopoverOpen(!isPopoverOpen)}> diff --git a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx index b26052f72e80c..cab4a7c04b368 100644 --- a/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/test_helpers/test_utils.test_helper.tsx @@ -48,6 +48,9 @@ export const mockKibanaProps: KibanaLogicProps = { }, }, data: dataPluginMock.createStartContract(), + esConfig: { + elasticsearch_host: 'https://your_deployment_url', + }, guidedOnboarding: {}, history: mockHistory, isSidebarEnabled: true, diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index ce116ee52c2a6..e7b9b862f3d5d 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -23,6 +23,7 @@ import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { LicensingPluginStart } from '@kbn/licensing-plugin/public'; import { MlPluginStart } from '@kbn/ml-plugin/public'; +import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; @@ -69,17 +70,29 @@ export interface PluginsStart { ml: MlPluginStart; } +export interface ESConfig { + elasticsearch_host: string; +} + export class EnterpriseSearchPlugin implements Plugin { private config: ClientConfigType; + private esConfig: ESConfig; constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); + this.esConfig = { elasticsearch_host: ELASTICSEARCH_URL_PLACEHOLDER }; } private data: ClientData = {} as ClientData; private async getInitialData(http: HttpSetup) { - if (!this.config.host && this.config.canDeployEntSearch) return; // No API to call + try { + this.esConfig = await http.get('/internal/enterprise_search/es_config'); + } catch { + this.esConfig = { elasticsearch_host: ELASTICSEARCH_URL_PLACEHOLDER }; + } + + if (!this.config.host) return; // No API to call if (this.hasInitialized) return; // We've already made an initial call try { @@ -113,7 +126,12 @@ export class EnterpriseSearchPlugin implements Plugin { private getPluginData() { // Small helper for grouping plugin data related args together - return { config: this.config, data: this.data, isSidebarEnabled: this.isSidebarEnabled }; + return { + config: this.config, + data: this.data, + esConfig: this.esConfig, + isSidebarEnabled: this.isSidebarEnabled, + }; } private hasInitialized: boolean = false; diff --git a/x-pack/plugins/enterprise_search/server/__mocks__/routerDependencies.mock.ts b/x-pack/plugins/enterprise_search/server/__mocks__/routerDependencies.mock.ts index 623b851b9ab7c..dba33ade88324 100644 --- a/x-pack/plugins/enterprise_search/server/__mocks__/routerDependencies.mock.ts +++ b/x-pack/plugins/enterprise_search/server/__mocks__/routerDependencies.mock.ts @@ -10,6 +10,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { mlPluginServerMock } from '@kbn/ml-plugin/server/mocks'; import { ConfigType } from '..'; +import { GlobalConfigService } from '../services/global_config_service'; export const mockLogger = loggingSystemMock.createLogger().get(); @@ -36,6 +37,7 @@ export const mockConfig = { export const mockDependencies = { // Mock router should be handled on a per-test basis config: mockConfig, + globalConfigService: new GlobalConfigService(), log: mockLogger, enterpriseSearchRequestHandler: mockRequestHandler as any, ml: mockMl, diff --git a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts index e19cdc7153215..ab4b27ed1f1c2 100644 --- a/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/check_access.test.ts @@ -7,6 +7,8 @@ import { spacesMock } from '@kbn/spaces-plugin/server/mocks'; +import { GlobalConfigService } from '../services/global_config_service'; + import { checkAccess } from './check_access'; jest.mock('./enterprise_search_config_api', () => ({ @@ -51,6 +53,7 @@ describe('checkAccess', () => { canDeployEntSearch: true, host: 'http://localhost:3002', }, + globalConfigService: new GlobalConfigService(), security: mockSecurity, spaces: mockSpaces, } as any; diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts index ae6f7b4607653..fb17854f6f674 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_config_api.test.ts @@ -19,6 +19,8 @@ jest.mock('@kbn/repo-info', () => ({ import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { GlobalConfigService } from '../services/global_config_service'; + import { callEnterpriseSearchConfigAPI, warnMismatchedVersions, @@ -37,6 +39,7 @@ describe('callEnterpriseSearchConfigAPI', () => { }; const mockDependencies = { config: mockConfig, + globalConfigService: new GlobalConfigService(), request: mockRequest, log: loggingSystemMock.create().get(), } as any; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 1132de4fbcccf..7a764e9ef6fb5 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -77,6 +77,7 @@ import { appSearchTelemetryType } from './saved_objects/app_search/telemetry'; import { enterpriseSearchTelemetryType } from './saved_objects/enterprise_search/telemetry'; import { workplaceSearchTelemetryType } from './saved_objects/workplace_search/telemetry'; +import { GlobalConfigService } from './services/global_config_service'; import { uiSettings as enterpriseSearchUISettings } from './ui_settings'; import { getSearchResultProvider } from './utils/search_result_provider'; @@ -104,9 +105,8 @@ export interface PluginsStart { export interface RouteDependencies { config: ConfigType; enterpriseSearchRequestHandler: IEnterpriseSearchRequestHandler; - getSavedObjectsService?(): SavedObjectsServiceStart; - + globalConfigService: GlobalConfigService; log: Logger; ml?: MlPluginSetup; router: IRouter; @@ -115,6 +115,7 @@ export interface RouteDependencies { export class EnterpriseSearchPlugin implements Plugin { private readonly config: ConfigType; private readonly logger: Logger; + private readonly globalConfigService: GlobalConfigService; /** * Exposed services @@ -122,11 +123,19 @@ export class EnterpriseSearchPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) { this.config = initializerContext.config.get(); + this.globalConfigService = new GlobalConfigService(); this.logger = initializerContext.logger.get(); } public setup( - { capabilities, http, savedObjects, getStartServices, uiSettings }: CoreSetup, + { + capabilities, + elasticsearch, + http, + savedObjects, + getStartServices, + uiSettings, + }: CoreSetup, { usageCollection, security, @@ -139,6 +148,7 @@ export class EnterpriseSearchPlugin implements Plugin { cloud, }: PluginsSetup ) { + this.globalConfigService.setup(elasticsearch.legacy.config$, cloud); const config = this.config; const log = this.logger; const PLUGIN_IDS = [ @@ -185,7 +195,14 @@ export class EnterpriseSearchPlugin implements Plugin { async (request: KibanaRequest) => { const [, { spaces }] = await getStartServices(); - const dependencies = { config, security, spaces, request, log, ml }; + const dependencies = { + config, + security, + spaces, + request, + log, + ml, + }; const { hasAppSearchAccess, hasWorkplaceSearchAccess } = await checkAccess(dependencies); const showEnterpriseSearch = @@ -228,7 +245,14 @@ export class EnterpriseSearchPlugin implements Plugin { */ const router = http.createRouter(); const enterpriseSearchRequestHandler = new EnterpriseSearchRequestHandler({ config, log }); - const dependencies = { router, config, log, enterpriseSearchRequestHandler, ml }; + const dependencies = { + router, + config, + globalConfigService: this.globalConfigService, + log, + enterpriseSearchRequestHandler, + ml, + }; registerConfigDataRoute(dependencies); if (config.canDeployEntSearch) registerAppSearchRoutes(dependencies); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts index e65941cb7f20e..7983c2c88b5d9 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/config_data.ts @@ -18,7 +18,12 @@ const errorMessage = i18n.translate( } ); -export function registerConfigDataRoute({ router, config, log }: RouteDependencies) { +export function registerConfigDataRoute({ + router, + config, + log, + globalConfigService, +}: RouteDependencies) { router.get( { path: '/internal/enterprise_search/config_data', @@ -45,4 +50,17 @@ export function registerConfigDataRoute({ router, config, log }: RouteDependenci } }) ); + + router.get( + { + path: '/internal/enterprise_search/es_config', + validate: false, + }, + elasticsearchErrorHandler(log, async (context, request, response) => { + return response.ok({ + body: { elasticsearch_host: globalConfigService.elasticsearchUrl }, + headers: { 'content-type': 'application/json' }, + }); + }) + ); } diff --git a/x-pack/plugins/enterprise_search/server/services/global_config_service.ts b/x-pack/plugins/enterprise_search/server/services/global_config_service.ts new file mode 100644 index 0000000000000..ecb2edac98644 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/services/global_config_service.ts @@ -0,0 +1,57 @@ +/* + * 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 { Observable, Subscription } from 'rxjs'; + +import { CloudSetup } from '@kbn/cloud-plugin/server'; +import { ElasticsearchConfig } from '@kbn/core/server'; + +export class GlobalConfigService { + /** + * + */ + + private cloudUrl?: string; + + /** + * An observable that emits elasticsearch config. + */ + private config$?: Observable; + + /** + * A reference to the subscription to the elasticsearch observable + */ + private configSub?: Subscription; + + public get elasticsearchUrl(): string { + return this.cloudUrl + ? this.cloudUrl + : this.globalConfigElasticsearchUrl || 'https://your_deployment_url'; + } + + /** + * The elasticsearch config value at a given point in time. + */ + private globalConfigElasticsearchUrl?: string; + + setup(config$: Observable, cloud: CloudSetup) { + this.cloudUrl = cloud.elasticsearchUrl; + this.config$ = config$; + this.configSub = this.config$.subscribe((config) => { + const rawHost = config.hosts[0]; + // strip username, password, URL params and other potentially sensitive info from hosts URL + const hostUrl = new URL(rawHost); + this.globalConfigElasticsearchUrl = `${hostUrl.origin}${hostUrl.pathname}`; + }); + } + + stop() { + if (this.configSub) { + this.configSub.unsubscribe(); + } + } +}