From 20860c7fb9352c34e673eb31b66cf2cead008055 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Tue, 10 Nov 2020 11:51:30 -0500 Subject: [PATCH] [Maps] add on-prem EMS config (#82525) (#83060) Adds.a new `map.emsUrl` setting. User can configure this to the location of a local on-prem installation of EMS. For this setting to take effect, the cluster must be configured with an enterprise license. --- .../maps_legacy/common/ems_defaults.ts | 25 ++ src/plugins/maps_legacy/common/index.ts | 20 ++ src/plugins/maps_legacy/config.ts | 19 +- src/plugins/maps_legacy/kibana.json | 1 + src/plugins/maps_legacy/public/index.ts | 1 + .../public/map/service_settings.js | 2 +- src/plugins/maps_legacy/server/index.ts | 1 + .../plugins/maps/common/ems_settings.test.ts | 221 ++++++++++++++++++ x-pack/plugins/maps/common/ems_settings.ts | 91 ++++++++ .../ems_boundaries_layer_wizard.tsx | 5 +- .../ems_base_map_layer_wizard.tsx | 5 +- .../components/ems_unavailable_message.tsx | 7 +- x-pack/plugins/maps/public/kibana_services.ts | 17 +- .../plugins/maps/public/licensed_features.ts | 8 + x-pack/plugins/maps/public/meta.test.js | 42 +++- x-pack/plugins/maps/public/meta.ts | 31 ++- x-pack/plugins/maps/public/plugin.ts | 11 +- .../bootstrap/get_initial_layers.test.js | 18 +- .../routing/bootstrap/get_initial_layers.ts | 4 +- x-pack/plugins/maps/server/plugin.ts | 20 +- x-pack/plugins/maps/server/routes.js | 80 ++++--- 21 files changed, 532 insertions(+), 97 deletions(-) create mode 100644 src/plugins/maps_legacy/common/ems_defaults.ts create mode 100644 src/plugins/maps_legacy/common/index.ts create mode 100644 x-pack/plugins/maps/common/ems_settings.test.ts create mode 100644 x-pack/plugins/maps/common/ems_settings.ts diff --git a/src/plugins/maps_legacy/common/ems_defaults.ts b/src/plugins/maps_legacy/common/ems_defaults.ts new file mode 100644 index 00000000000000..583dca1dbf036c --- /dev/null +++ b/src/plugins/maps_legacy/common/ems_defaults.ts @@ -0,0 +1,25 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Default config for the elastic hosted EMS endpoints +export const DEFAULT_EMS_FILE_API_URL = 'https://vector.maps.elastic.co'; +export const DEFAULT_EMS_TILE_API_URL = 'https://tiles.maps.elastic.co'; +export const DEFAULT_EMS_LANDING_PAGE_URL = 'https://maps.elastic.co/v7.10'; +export const DEFAULT_EMS_FONT_LIBRARY_URL = + 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; diff --git a/src/plugins/maps_legacy/common/index.ts b/src/plugins/maps_legacy/common/index.ts new file mode 100644 index 00000000000000..12148ec1ec6b81 --- /dev/null +++ b/src/plugins/maps_legacy/common/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './ems_defaults'; diff --git a/src/plugins/maps_legacy/config.ts b/src/plugins/maps_legacy/config.ts index f49d56dedd45f5..68595944e68b3f 100644 --- a/src/plugins/maps_legacy/config.ts +++ b/src/plugins/maps_legacy/config.ts @@ -21,18 +21,29 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { configSchema as tilemapSchema } from '../tile_map/config'; import { configSchema as regionmapSchema } from '../region_map/config'; +import { + DEFAULT_EMS_FONT_LIBRARY_URL, + DEFAULT_EMS_LANDING_PAGE_URL, + DEFAULT_EMS_TILE_API_URL, + DEFAULT_EMS_FILE_API_URL, +} from './common/ems_defaults'; + export const configSchema = schema.object({ includeElasticMapsService: schema.boolean({ defaultValue: true }), proxyElasticMapsServiceInMaps: schema.boolean({ defaultValue: false }), tilemap: tilemapSchema, regionmap: regionmapSchema, manifestServiceUrl: schema.string({ defaultValue: '' }), - emsFileApiUrl: schema.string({ defaultValue: 'https://vector.maps.elastic.co' }), - emsTileApiUrl: schema.string({ defaultValue: 'https://tiles.maps.elastic.co' }), - emsLandingPageUrl: schema.string({ defaultValue: 'https://maps.elastic.co/v7.10' }), + + emsUrl: schema.string({ defaultValue: '' }), + + emsFileApiUrl: schema.string({ defaultValue: DEFAULT_EMS_FILE_API_URL }), + emsTileApiUrl: schema.string({ defaultValue: DEFAULT_EMS_TILE_API_URL }), + emsLandingPageUrl: schema.string({ defaultValue: DEFAULT_EMS_LANDING_PAGE_URL }), emsFontLibraryUrl: schema.string({ - defaultValue: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf', + defaultValue: DEFAULT_EMS_FONT_LIBRARY_URL, }), + emsTileLayerId: schema.object({ bright: schema.string({ defaultValue: 'road_map' }), desaturated: schema.string({ defaultValue: 'road_map_desaturated' }), diff --git a/src/plugins/maps_legacy/kibana.json b/src/plugins/maps_legacy/kibana.json index d9bf33e6613684..1499b3de446b5b 100644 --- a/src/plugins/maps_legacy/kibana.json +++ b/src/plugins/maps_legacy/kibana.json @@ -5,5 +5,6 @@ "configPath": ["map"], "ui": true, "server": true, + "extraPublicDirs": ["common"], "requiredBundles": ["kibanaReact", "charts"] } diff --git a/src/plugins/maps_legacy/public/index.ts b/src/plugins/maps_legacy/public/index.ts index fe5338b890ec83..2654ded907cce9 100644 --- a/src/plugins/maps_legacy/public/index.ts +++ b/src/plugins/maps_legacy/public/index.ts @@ -59,6 +59,7 @@ export { mapTooltipProvider, }; +export * from '../common'; export * from './common/types'; export { ORIGIN } from './common/constants/origin'; diff --git a/src/plugins/maps_legacy/public/map/service_settings.js b/src/plugins/maps_legacy/public/map/service_settings.js index 833304378402a1..7a00456b89a92e 100644 --- a/src/plugins/maps_legacy/public/map/service_settings.js +++ b/src/plugins/maps_legacy/public/map/service_settings.js @@ -128,7 +128,7 @@ export class ServiceSettings { allServices.push(tmsService); } - if (this._mapConfig.includeElasticMapsService) { + if (this._mapConfig.includeElasticMapsService && !this._mapConfig.emsUrl) { const servicesFromManifest = await this._emsClient.getTMSServices(); const strippedServiceFromManifest = await Promise.all( servicesFromManifest diff --git a/src/plugins/maps_legacy/server/index.ts b/src/plugins/maps_legacy/server/index.ts index 665b3b8986ef0e..ba37df60f93579 100644 --- a/src/plugins/maps_legacy/server/index.ts +++ b/src/plugins/maps_legacy/server/index.ts @@ -30,6 +30,7 @@ export const config: PluginConfigDescriptor = { tilemap: true, regionmap: true, manifestServiceUrl: true, + emsUrl: true, emsFileApiUrl: true, emsTileApiUrl: true, emsLandingPageUrl: true, diff --git a/x-pack/plugins/maps/common/ems_settings.test.ts b/x-pack/plugins/maps/common/ems_settings.test.ts new file mode 100644 index 00000000000000..69ae7069129cbf --- /dev/null +++ b/x-pack/plugins/maps/common/ems_settings.test.ts @@ -0,0 +1,221 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EMSSettings, IEMSConfig } from './ems_settings'; +import { + DEFAULT_EMS_FILE_API_URL, + DEFAULT_EMS_FONT_LIBRARY_URL, + DEFAULT_EMS_LANDING_PAGE_URL, + DEFAULT_EMS_TILE_API_URL, +} from '../../../../src/plugins/maps_legacy/common'; + +const IS_ENTERPRISE_PLUS = () => true; + +describe('EMSSettings', () => { + const mockConfig: IEMSConfig = { + includeElasticMapsService: true, + proxyElasticMapsServiceInMaps: false, + emsUrl: '', + emsFileApiUrl: DEFAULT_EMS_FILE_API_URL, + emsTileApiUrl: DEFAULT_EMS_TILE_API_URL, + emsLandingPageUrl: DEFAULT_EMS_LANDING_PAGE_URL, + emsFontLibraryUrl: DEFAULT_EMS_FONT_LIBRARY_URL, + isEMSEnabled: true, + }; + + describe('isEMSEnabled/isOnPrem', () => { + test('should validate defaults', () => { + const emsSettings = new EMSSettings(mockConfig, IS_ENTERPRISE_PLUS); + expect(emsSettings.isEMSEnabled()).toBe(true); + expect(emsSettings.isOnPrem()).toBe(false); + }); + + test('should validate if on-prem is turned on', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.isEMSEnabled()).toBe(true); + expect(emsSettings.isOnPrem()).toBe(true); + }); + + test('should not validate if ems turned off', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + includeElasticMapsService: false, + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.isEMSEnabled()).toBe(false); + expect(emsSettings.isOnPrem()).toBe(false); + }); + + test('should work if ems is turned off, but on-prem is turned on', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + includeElasticMapsService: false, + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.isEMSEnabled()).toBe(true); + expect(emsSettings.isOnPrem()).toBe(true); + }); + + describe('when license is turned off', () => { + test('should not be enabled', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + }, + }, + () => false + ); + expect(emsSettings.isEMSEnabled()).toBe(false); + expect(emsSettings.isOnPrem()).toBe(true); + }); + }); + }); + + describe('emsUrl setting', () => { + describe('when emsUrl is not set', () => { + test('should respect defaults', () => { + const emsSettings = new EMSSettings(mockConfig, IS_ENTERPRISE_PLUS); + expect(emsSettings.getEMSFileApiUrl()).toBe(DEFAULT_EMS_FILE_API_URL); + expect(emsSettings.getEMSTileApiUrl()).toBe(DEFAULT_EMS_TILE_API_URL); + expect(emsSettings.getEMSFontLibraryUrl()).toBe(DEFAULT_EMS_FONT_LIBRARY_URL); + expect(emsSettings.getEMSLandingPageUrl()).toBe(DEFAULT_EMS_LANDING_PAGE_URL); + }); + test('should apply overrides', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsFileApiUrl: 'https://file.foobar', + emsTileApiUrl: 'https://tile.foobar', + emsFontLibraryUrl: 'https://tile.foobar/font', + emsLandingPageUrl: 'https://maps.foobar/v7.666', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://file.foobar'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://tile.foobar'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe('https://tile.foobar/font'); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://maps.foobar/v7.666'); + }); + }); + + describe('when emsUrl is set', () => { + test('should override defaults', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://localhost:8080/file'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://localhost:8080/tile'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe( + 'https://localhost:8080/tile/fonts/{fontstack}/{range}.pbf' + ); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://localhost:8080/maps'); + }); + + describe('internal settings overrides (the below behavior is not publically supported, but aids internal debugging use-cases)', () => { + test(`should override internal emsFileApiUrl`, () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + emsFileApiUrl: 'https://file.foobar', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://file.foobar'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://localhost:8080/tile'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe( + 'https://localhost:8080/tile/fonts/{fontstack}/{range}.pbf' + ); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://localhost:8080/maps'); + }); + + test(`should override internal emsTileApiUrl`, () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + emsTileApiUrl: 'https://tile.foobar', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://localhost:8080/file'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://tile.foobar'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe( + 'https://localhost:8080/tile/fonts/{fontstack}/{range}.pbf' + ); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://localhost:8080/maps'); + }); + + test('should override internal emsFontLibraryUrl', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + emsFontLibraryUrl: 'https://maps.foobar/fonts', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://localhost:8080/file'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://localhost:8080/tile'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe('https://maps.foobar/fonts'); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://localhost:8080/maps'); + }); + + test('should override internal emsLandingPageUrl', () => { + const emsSettings = new EMSSettings( + { + ...mockConfig, + ...{ + emsUrl: 'https://localhost:8080', + emsLandingPageUrl: 'https://maps.foobar', + }, + }, + IS_ENTERPRISE_PLUS + ); + expect(emsSettings.getEMSFileApiUrl()).toBe('https://localhost:8080/file'); + expect(emsSettings.getEMSTileApiUrl()).toBe('https://localhost:8080/tile'); + expect(emsSettings.getEMSFontLibraryUrl()).toBe( + 'https://localhost:8080/tile/fonts/{fontstack}/{range}.pbf' + ); + expect(emsSettings.getEMSLandingPageUrl()).toBe('https://maps.foobar'); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/maps/common/ems_settings.ts b/x-pack/plugins/maps/common/ems_settings.ts new file mode 100644 index 00000000000000..0f4d211f0e9632 --- /dev/null +++ b/x-pack/plugins/maps/common/ems_settings.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + DEFAULT_EMS_FILE_API_URL, + DEFAULT_EMS_FONT_LIBRARY_URL, + DEFAULT_EMS_LANDING_PAGE_URL, + DEFAULT_EMS_TILE_API_URL, +} from '../../../../src/plugins/maps_legacy/common'; + +export interface IEMSConfig { + emsUrl?: string; + includeElasticMapsService?: boolean; + proxyElasticMapsServiceInMaps?: boolean; + emsFileApiUrl?: string; + emsTileApiUrl?: string; + emsLandingPageUrl?: string; + emsFontLibraryUrl?: string; + isEMSEnabled?: boolean; +} + +export class EMSSettings { + private readonly _config: IEMSConfig; + private readonly _getIsEnterprisePlus: () => boolean; + + constructor(config: IEMSConfig, getIsEnterPrisePlus: () => boolean) { + this._config = config; + this._getIsEnterprisePlus = getIsEnterPrisePlus; + } + + _isEMSUrlSet() { + return !!this._config.emsUrl; + } + + _getEMSRoot() { + return this._config.emsUrl!.replace(/\/$/, ''); + } + + isOnPrem(): boolean { + return this._isEMSUrlSet(); + } + + isIncludeElasticMapsService() { + return !!this._config.includeElasticMapsService; + } + + isEMSEnabled(): boolean { + if (this._isEMSUrlSet()) { + return this._getIsEnterprisePlus(); + } + return this.isIncludeElasticMapsService(); + } + + getEMSFileApiUrl(): string { + if (this._config.emsFileApiUrl !== DEFAULT_EMS_FILE_API_URL || !this._isEMSUrlSet()) { + return this._config.emsFileApiUrl!; + } else { + return `${this._getEMSRoot()}/file`; + } + } + + isProxyElasticMapsServiceInMaps(): boolean { + return !!this._config.proxyElasticMapsServiceInMaps; + } + + getEMSTileApiUrl(): string { + if (this._config.emsTileApiUrl !== DEFAULT_EMS_TILE_API_URL || !this._isEMSUrlSet()) { + return this._config.emsTileApiUrl!; + } else { + return `${this._getEMSRoot()}/tile`; + } + } + getEMSLandingPageUrl(): string { + if (this._config.emsLandingPageUrl !== DEFAULT_EMS_LANDING_PAGE_URL || !this._isEMSUrlSet()) { + return this._config.emsLandingPageUrl!; + } else { + return `${this._getEMSRoot()}/maps`; + } + } + + getEMSFontLibraryUrl(): string { + if (this._config.emsFontLibraryUrl !== DEFAULT_EMS_FONT_LIBRARY_URL || !this._isEMSUrlSet()) { + return this._config.emsFontLibraryUrl!; + } else { + return `${this._getEMSRoot()}/tile/fonts/{fontstack}/{range}.pbf`; + } + } +} diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx index 8d4d57e5242761..768bbd1d947002 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_boundaries_layer_wizard.tsx @@ -11,14 +11,15 @@ import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_re import { EMSFileCreateSourceEditor } from './create_source_editor'; import { EMSFileSource, sourceTitle } from './ems_file_source'; // @ts-ignore -import { getIsEmsEnabled } from '../../../kibana_services'; +import { getEMSSettings } from '../../../kibana_services'; import { EMSFileSourceDescriptor } from '../../../../common/descriptor_types'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; export const emsBoundariesLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.REFERENCE], checkVisibility: async () => { - return getIsEmsEnabled(); + const emsSettings = getEMSSettings(); + return emsSettings!.isEMSEnabled(); }, description: i18n.translate('xpack.maps.source.emsFileDescription', { defaultMessage: 'Administrative boundaries from Elastic Maps Service', diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx index 315759a2eba293..bfa46574f007ab 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_base_map_layer_wizard.tsx @@ -13,13 +13,14 @@ import { EMSTMSSource, sourceTitle } from './ems_tms_source'; import { VectorTileLayer } from '../../layers/vector_tile_layer/vector_tile_layer'; // @ts-ignore import { TileServiceSelect } from './tile_service_select'; -import { getIsEmsEnabled } from '../../../kibana_services'; +import { getEMSSettings } from '../../../kibana_services'; import { LAYER_WIZARD_CATEGORY } from '../../../../common/constants'; export const emsBaseMapLayerWizardConfig: LayerWizard = { categories: [LAYER_WIZARD_CATEGORY.REFERENCE], checkVisibility: async () => { - return getIsEmsEnabled(); + const emsSettings = getEMSSettings(); + return emsSettings!.isEMSEnabled(); }, description: i18n.translate('xpack.maps.source.emsTileDescription', { defaultMessage: 'Tile map service from Elastic Maps Service', diff --git a/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx b/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx index dea161fafd609e..ba897b7d9da0c5 100644 --- a/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx +++ b/x-pack/plugins/maps/public/components/ems_unavailable_message.tsx @@ -5,15 +5,14 @@ */ import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { getIsEmsEnabled } from '../kibana_services'; +import { getEMSSettings } from '../kibana_services'; export function getEmsUnavailableMessage(): string { - const isEmsEnabled = getIsEmsEnabled(); + const isEmsEnabled = getEMSSettings().isEMSEnabled(); if (isEmsEnabled) { return i18n.translate('xpack.maps.source.ems.noAccessDescription', { defaultMessage: - 'Kibana is unable to access Elastic Maps Service. Contact your system administrator', + 'Kibana is unable to access Elastic Maps Service. Contact your system administrator.', }); } diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index 08ee4b6628dd13..782c37a72d99b4 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -9,6 +9,7 @@ import { CoreStart } from 'kibana/public'; import { MapsLegacyConfig } from '../../../../src/plugins/maps_legacy/config'; import { MapsConfigType } from '../config'; import { MapsPluginStartDependencies } from './plugin'; +import { EMSSettings } from '../common/ems_settings'; let kibanaVersion: string; export const setKibanaVersion = (version: string) => (kibanaVersion = version); @@ -62,14 +63,16 @@ let kibanaCommonConfig: MapsLegacyConfig; export const setKibanaCommonConfig = (config: MapsLegacyConfig) => (kibanaCommonConfig = config); export const getKibanaCommonConfig = () => kibanaCommonConfig; -export const getIsEmsEnabled = () => getKibanaCommonConfig().includeElasticMapsService; -export const getEmsFontLibraryUrl = () => getKibanaCommonConfig().emsFontLibraryUrl; +let emsSettings: EMSSettings; +export const setEMSSettings = (value: EMSSettings) => { + emsSettings = value; +}; +export const getEMSSettings = () => { + return emsSettings; +}; + export const getEmsTileLayerId = () => getKibanaCommonConfig().emsTileLayerId; -export const getEmsFileApiUrl = () => getKibanaCommonConfig().emsFileApiUrl; -export const getEmsTileApiUrl = () => getKibanaCommonConfig().emsTileApiUrl; -export const getEmsLandingPageUrl = () => getKibanaCommonConfig().emsLandingPageUrl; -export const getProxyElasticMapsServiceInMaps = () => - getKibanaCommonConfig().proxyElasticMapsServiceInMaps; + export const getRegionmapLayers = () => _.get(getKibanaCommonConfig(), 'regionmap.layers', []); export const getTilemap = () => _.get(getKibanaCommonConfig(), 'tilemap', []); diff --git a/x-pack/plugins/maps/public/licensed_features.ts b/x-pack/plugins/maps/public/licensed_features.ts index 67fa526da0cbdb..13809f2b26a8c6 100644 --- a/x-pack/plugins/maps/public/licensed_features.ts +++ b/x-pack/plugins/maps/public/licensed_features.ts @@ -27,9 +27,13 @@ export const LICENCED_FEATURES_DETAILS: Record licenseId; export const getIsGoldPlus = () => isGoldPlus; +export const getIsEnterprisePlus = () => isEnterprisePlus; + export function registerLicensedFeatures(licensingPlugin: LicensingPluginSetup) { for (const licensedFeature of Object.values(LICENSED_FEATURES)) { licensingPlugin.featureUsage.register( @@ -45,6 +49,10 @@ export function setLicensingPluginStart(licensingPlugin: LicensingPluginStart) { licensingPluginStart.license$.subscribe((license: ILicense) => { const gold = license.check(APP_ID, 'gold'); isGoldPlus = gold.state === 'valid'; + + const enterprise = license.check(APP_ID, 'enterprise'); + isEnterprisePlus = enterprise.state === 'valid'; + licenseId = license.uid; }); } diff --git a/x-pack/plugins/maps/public/meta.test.js b/x-pack/plugins/maps/public/meta.test.js index c414c8a2d400e5..d4f9885830b54d 100644 --- a/x-pack/plugins/maps/public/meta.test.js +++ b/x-pack/plugins/maps/public/meta.test.js @@ -9,14 +9,22 @@ import { getEMSClient, getGlyphUrl } from './meta'; jest.mock('@elastic/ems-client'); +const EMS_FONTS_URL_MOCK = 'ems/fonts'; +const MOCK_EMS_SETTINGS = { + isEMSEnabled: () => true, + getEMSFileApiUrl: () => 'https://file-api', + getEMSTileApiUrl: () => 'https://tile-api', + getEMSLandingPageUrl: () => 'http://test.com', + getEMSFontLibraryUrl: () => EMS_FONTS_URL_MOCK, + isProxyElasticMapsServiceInMaps: () => false, +}; + describe('default use without proxy', () => { beforeEach(() => { - require('./kibana_services').getProxyElasticMapsServiceInMaps = () => false; - require('./kibana_services').getIsEmsEnabled = () => true; require('./kibana_services').getEmsTileLayerId = () => '123'; - require('./kibana_services').getEmsFileApiUrl = () => 'https://file-api'; - require('./kibana_services').getEmsTileApiUrl = () => 'https://tile-api'; - require('./kibana_services').getEmsLandingPageUrl = () => 'http://test.com'; + require('./kibana_services').getEMSSettings = () => { + return MOCK_EMS_SETTINGS; + }; require('./licensed_features').getLicenseId = () => { return 'foobarlicenseid'; }; @@ -32,10 +40,7 @@ describe('default use without proxy', () => { describe('getGlyphUrl', () => { describe('EMS enabled', () => { - const EMS_FONTS_URL_MOCK = 'ems/fonts'; beforeAll(() => { - require('./kibana_services').getIsEmsEnabled = () => true; - require('./kibana_services').getEmsFontLibraryUrl = () => EMS_FONTS_URL_MOCK; require('./kibana_services').getHttp = () => ({ basePath: { prepend: (url) => url, // No need to actually prepend a dev basepath for test @@ -45,7 +50,12 @@ describe('getGlyphUrl', () => { describe('EMS proxy enabled', () => { beforeAll(() => { - require('./kibana_services').getProxyElasticMapsServiceInMaps = () => true; + require('./kibana_services').getEMSSettings = () => { + return { + ...MOCK_EMS_SETTINGS, + isProxyElasticMapsServiceInMaps: () => true, + }; + }; }); test('should return proxied EMS fonts URL', async () => { @@ -55,7 +65,12 @@ describe('getGlyphUrl', () => { describe('EMS proxy disabled', () => { beforeAll(() => { - require('./kibana_services').getProxyElasticMapsServiceInMaps = () => false; + require('./kibana_services').getEMSSettings = () => { + return { + ...MOCK_EMS_SETTINGS, + isProxyElasticMapsServiceInMaps: () => false, + }; + }; }); test('should return EMS fonts URL', async () => { @@ -72,7 +87,12 @@ describe('getGlyphUrl', () => { }, }; require('./kibana_services').getHttp = () => mockHttp; - require('./kibana_services').getIsEmsEnabled = () => false; + require('./kibana_services').getEMSSettings = () => { + return { + ...MOCK_EMS_SETTINGS, + isEMSEnabled: () => false, + }; + }; }); test('should return kibana fonts URL', async () => { diff --git a/x-pack/plugins/maps/public/meta.ts b/x-pack/plugins/maps/public/meta.ts index 929050338de729..5632e226478a7a 100644 --- a/x-pack/plugins/maps/public/meta.ts +++ b/x-pack/plugins/maps/public/meta.ts @@ -18,15 +18,10 @@ import { } from '../common/constants'; import { getHttp, - getIsEmsEnabled, getRegionmapLayers, getTilemap, - getEmsFileApiUrl, - getEmsTileApiUrl, - getEmsLandingPageUrl, - getEmsFontLibraryUrl, - getProxyElasticMapsServiceInMaps, getKibanaVersion, + getEMSSettings, } from './kibana_services'; import { getLicenseId } from './licensed_features'; import { LayerConfig } from '../../../../src/plugins/region_map/config'; @@ -40,7 +35,7 @@ export function getKibanaTileMap(): unknown { } export async function getEmsFileLayers(): Promise { - if (!getIsEmsEnabled()) { + if (!getEMSSettings().isEMSEnabled()) { return []; } @@ -48,7 +43,7 @@ export async function getEmsFileLayers(): Promise { } export async function getEmsTmsServices(): Promise { - if (!getIsEmsEnabled()) { + if (!getEMSSettings().isEMSEnabled()) { return []; } @@ -65,18 +60,18 @@ let emsClient: EMSClient | null = null; let latestLicenseId: string | undefined; export function getEMSClient(): EMSClient { if (!emsClient) { - const proxyElasticMapsServiceInMaps = getProxyElasticMapsServiceInMaps(); + const emsSettings = getEMSSettings(); const proxyPath = ''; - const tileApiUrl = proxyElasticMapsServiceInMaps + const tileApiUrl = emsSettings!.isProxyElasticMapsServiceInMaps() ? relativeToAbsolute( getHttp().basePath.prepend(`/${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}`) ) - : getEmsTileApiUrl(); - const fileApiUrl = proxyElasticMapsServiceInMaps + : emsSettings!.getEMSTileApiUrl(); + const fileApiUrl = emsSettings!.isProxyElasticMapsServiceInMaps() ? relativeToAbsolute( getHttp().basePath.prepend(`/${GIS_API_PATH}/${EMS_FILES_CATALOGUE_PATH}`) ) - : getEmsFileApiUrl(); + : emsSettings!.getEMSFileApiUrl(); emsClient = new EMSClient({ language: i18n.getLocale(), @@ -84,7 +79,7 @@ export function getEMSClient(): EMSClient { appName: EMS_APP_NAME, tileApiUrl, fileApiUrl, - landingPageUrl: getEmsLandingPageUrl(), + landingPageUrl: emsSettings!.getEMSLandingPageUrl(), fetchFunction(url: string) { return fetch(url); }, @@ -100,16 +95,18 @@ export function getEMSClient(): EMSClient { } export function getGlyphUrl(): string { - if (!getIsEmsEnabled()) { + const emsSettings = getEMSSettings(); + if (!emsSettings!.isEMSEnabled()) { return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); } - return getProxyElasticMapsServiceInMaps() + + return emsSettings!.isProxyElasticMapsServiceInMaps() ? relativeToAbsolute( getHttp().basePath.prepend( `/${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}` ) ) + `/{fontstack}/{range}` - : getEmsFontLibraryUrl(); + : emsSettings!.getEMSFontLibraryUrl(); } export function isRetina(): boolean { diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 75a3f8ef5ede84..b79a2b06b9b373 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -19,6 +19,7 @@ import { // @ts-ignore import { MapView } from './inspector/views/map_view'; import { + setEMSSettings, setKibanaCommonConfig, setKibanaVersion, setMapAppConfig, @@ -55,7 +56,12 @@ import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; import { StartContract as FileUploadStartContract } from '../../file_upload/public'; import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; -import { registerLicensedFeatures, setLicensingPluginStart } from './licensed_features'; +import { + getIsEnterprisePlus, + registerLicensedFeatures, + setLicensingPluginStart, +} from './licensed_features'; +import { EMSSettings } from '../common/ems_settings'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -111,6 +117,9 @@ export class MapsPlugin setMapAppConfig(config); setKibanaVersion(this._initializerContext.env.packageInfo.version); + const emsSettings = new EMSSettings(plugins.mapsLegacy.config, getIsEnterprisePlus); + setEMSSettings(emsSettings); + // register url generators const getStartServices = async () => { const [coreStart] = await core.getStartServices(); diff --git a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.test.js b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.test.js index 4de29e6f028e15..66adb1da6900e7 100644 --- a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.test.js +++ b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.test.js @@ -15,7 +15,11 @@ const layerListNotProvided = undefined; describe('Saved object has layer list', () => { beforeEach(() => { - require('../../kibana_services').getIsEmsEnabled = () => true; + require('../../kibana_services').getEMSSettings = () => { + return { + isEMSEnabled: () => true, + }; + }; }); it('Should get initial layers from saved object', () => { @@ -65,7 +69,11 @@ describe('EMS is enabled', () => { require('../../meta').getKibanaTileMap = () => { return null; }; - require('../../kibana_services').getIsEmsEnabled = () => true; + require('../../kibana_services').getEMSSettings = () => { + return { + isEMSEnabled: () => true, + }; + }; require('../../kibana_services').getEmsTileLayerId = () => ({ bright: 'road_map', desaturated: 'road_map_desaturated', @@ -101,7 +109,11 @@ describe('EMS is not enabled', () => { require('../../meta').getKibanaTileMap = () => { return null; }; - require('../../kibana_services').getIsEmsEnabled = () => false; + require('../../kibana_services').getEMSSettings = () => { + return { + isEMSEnabled: () => false, + }; + }; }); it('Should return empty layer list since there are no configured tile layers', () => { diff --git a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.ts b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.ts index e828dc88409cb7..c8873208739958 100644 --- a/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.ts +++ b/x-pack/plugins/maps/public/routing/bootstrap/get_initial_layers.ts @@ -23,7 +23,7 @@ import { TileLayer } from '../../classes/layers/tile_layer/tile_layer'; import { EMSTMSSource } from '../../classes/sources/ems_tms_source'; // @ts-expect-error import { VectorTileLayer } from '../../classes/layers/vector_tile_layer/vector_tile_layer'; -import { getIsEmsEnabled, getToasts } from '../../kibana_services'; +import { getEMSSettings, getToasts } from '../../kibana_services'; import { INITIAL_LAYERS_KEY } from '../../../common/constants'; import { getKibanaTileMap } from '../../meta'; @@ -39,7 +39,7 @@ export function getInitialLayers(layerListJSON?: string, initialLayers: LayerDes return [layerDescriptor, ...initialLayers]; } - const isEmsEnabled = getIsEmsEnabled(); + const isEmsEnabled = getEMSSettings()!.isEMSEnabled(); if (isEmsEnabled) { const layerDescriptor = VectorTileLayer.createDescriptor({ sourceDescriptor: EMSTMSSource.createDescriptor({ isAutoSelect: true }), diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index 00950e96047a0e..65d79272494f0f 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -28,7 +28,7 @@ import { ILicense } from '../../licensing/common/types'; import { LicensingPluginSetup } from '../../licensing/server'; import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; import { MapsLegacyPluginSetup } from '../../../../src/plugins/maps_legacy/server'; -import { MapsLegacyConfig } from '../../../../src/plugins/maps_legacy/config'; +import { EMSSettings } from '../common/ems_settings'; interface SetupDeps { features: FeaturesPluginSetupContract; @@ -52,7 +52,7 @@ export class MapsPlugin implements Plugin { _initHomeData( home: HomeServerPluginSetup, prependBasePath: (path: string) => string, - mapsLegacyConfig: MapsLegacyConfig + emsSettings: EMSSettings ) { const sampleDataLinkLabel = i18n.translate('xpack.maps.sampleDataLinkLabel', { defaultMessage: 'Map', @@ -125,7 +125,7 @@ export class MapsPlugin implements Plugin { home.tutorials.registerTutorial( emsBoundariesSpecProvider({ prependBasePath, - emsLandingPageUrl: mapsLegacyConfig.emsLandingPageUrl, + emsLandingPageUrl: emsSettings.getEMSLandingPageUrl(), }) ); } @@ -148,21 +148,27 @@ export class MapsPlugin implements Plugin { } let routesInitialized = false; + let isEnterprisePlus = false; + const emsSettings = new EMSSettings(mapsLegacyConfig, () => isEnterprisePlus); licensing.license$.subscribe((license: ILicense) => { - const { state } = license.check('maps', 'basic'); - if (state === 'valid' && !routesInitialized) { + const basic = license.check(APP_ID, 'basic'); + + const enterprise = license.check(APP_ID, 'enterprise'); + isEnterprisePlus = enterprise.state === 'valid'; + + if (basic.state === 'valid' && !routesInitialized) { routesInitialized = true; initRoutes( core.http.createRouter(), license.uid, - mapsLegacyConfig, + emsSettings, this.kibanaVersion, this._logger ); } }); - this._initHomeData(home, core.http.basePath.prepend, mapsLegacyConfig); + this._initHomeData(home, core.http.basePath.prepend, emsSettings); features.registerKibanaFeature({ id: APP_ID, diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index 5feacfee4d4d29..49d646f9a4e6df 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -33,38 +33,45 @@ import fs from 'fs'; import path from 'path'; import { initMVTRoutes } from './mvt/mvt_routes'; -export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { +const EMPTY_EMS_CLIENT = { + async getFileLayers() { + return []; + }, + async getTMSServices() { + return []; + }, + async getMainManifest() { + return null; + }, + async getDefaultFileManifest() { + return null; + }, + async getDefaultTMSManifest() { + return null; + }, + addQueryParams() {}, +}; + +export function initRoutes(router, licenseUid, emsSettings, kbnVersion, logger) { let emsClient; - if (mapConfig.includeElasticMapsService) { + + if (emsSettings.isIncludeElasticMapsService()) { emsClient = new EMSClient({ language: i18n.getLocale(), appVersion: kbnVersion, appName: EMS_APP_NAME, - fileApiUrl: mapConfig.emsFileApiUrl, - tileApiUrl: mapConfig.emsTileApiUrl, - landingPageUrl: mapConfig.emsLandingPageUrl, + fileApiUrl: emsSettings.getEMSFileApiUrl(), + tileApiUrl: emsSettings.getEMSTileApiUrl(), + landingPageUrl: emsSettings.getEMSLandingPageUrl(), fetchFunction: fetch, }); emsClient.addQueryParams({ license: licenseUid }); } else { - emsClient = { - async getFileLayers() { - return []; - }, - async getTMSServices() { - return []; - }, - async getMainManifest() { - return null; - }, - async getDefaultFileManifest() { - return null; - }, - async getDefaultTMSManifest() { - return null; - }, - addQueryParams() {}, - }; + emsClient = EMPTY_EMS_CLIENT; + } + + function getEMSClient() { + return emsSettings.isEMSEnabled() ? emsClient : EMPTY_EMS_CLIENT; } router.get( @@ -90,7 +97,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return null; } - const fileLayers = await emsClient.getFileLayers(); + const fileLayers = await getEMSClient().getFileLayers(); const layer = fileLayers.find((layer) => layer.getId() === request.query.id); if (!layer) { return null; @@ -127,7 +134,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return null; } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { return null; @@ -153,7 +160,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const main = await emsClient.getMainManifest(); + const main = await getEMSClient().getMainManifest(); const proxiedManifest = { services: [], }; @@ -189,8 +196,8 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const file = await emsClient.getDefaultFileManifest(); //need raw manifest - const fileLayers = await emsClient.getFileLayers(); + const file = await getEMSClient().getDefaultFileManifest(); //need raw manifest + const fileLayers = await getEMSClient().getFileLayers(); const layers = file.layers.map((layerJson) => { const newLayerJson = { ...layerJson }; @@ -231,7 +238,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const tilesManifest = await emsClient.getDefaultTMSManifest(); + const tilesManifest = await getEMSClient().getDefaultTMSManifest(); const newServices = tilesManifest.services.map((service) => { const newService = { ...service, @@ -284,7 +291,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return null; } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { return null; @@ -319,7 +326,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { return null; @@ -368,7 +375,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { return null; @@ -409,7 +416,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.query.id); if (!tmsService) { return null; @@ -439,7 +446,8 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { if (!checkEMSProxyEnabled()) { return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const url = mapConfig.emsFontLibraryUrl + const url = emsSettings + .getEMSFontLibraryUrl() .replace('{fontstack}', request.params.fontstack) .replace('{range}', request.params.range); @@ -469,7 +477,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { return response.badRequest('map.proxyElasticMapsServiceInMaps disabled'); } - const tmsServices = await emsClient.getTMSServices(); + const tmsServices = await getEMSClient().getTMSServices(); const tmsService = tmsServices.find((layer) => layer.getId() === request.params.id); if (!tmsService) { return null; @@ -573,7 +581,7 @@ export function initRoutes(router, licenseUid, mapConfig, kbnVersion, logger) { ); function checkEMSProxyEnabled() { - const proxyEMSInMaps = mapConfig.proxyElasticMapsServiceInMaps; + const proxyEMSInMaps = emsSettings.isProxyElasticMapsServiceInMaps(); if (!proxyEMSInMaps) { logger.warn( `Cannot load content from EMS when map.proxyElasticMapsServiceInMaps is turned off`