diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.test.ts b/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.test.ts new file mode 100644 index 00000000000000..18cbd8c17f03b6 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.test.ts @@ -0,0 +1,122 @@ +/* + * 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 { getGlyphs, getCanAccessEmsFonts, testOnlyClearCanAccessEmsFontsPromise } from './glyphs'; + +jest.mock('../../kibana_services', () => ({ + getHttp: () => { + return { + basePath: { + prepend: (path: string) => `abc${path}`, + }, + }; + }, + getDocLinks: () => { + return { + links: { + maps: { + connectToEms: 'https://www.elastic.co/guide/en/kibana/current/maps-connect-to-ems.html', + }, + }, + }; + }, +})); + +describe('EMS enabled', () => { + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../../kibana_services').getEMSSettings = () => { + return { + getEMSFontLibraryUrl: () => { + return 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; + }, + isEMSEnabled() { + return true; + }, + }; + }; + testOnlyClearCanAccessEmsFontsPromise(); + }); + + describe('offline', () => { + beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('node-fetch').default = () => { + throw new Error('Simulated offline environment with no EMS access'); + }; + }); + + test('should return EMS fonts template URL before canAccessEmsFontsPromise resolves', () => { + expect(getGlyphs()).toEqual({ + isEmsFont: true, + glyphUrlTemplate: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf', + }); + }); + + test('should return kibana fonts template URL after canAccessEmsFontsPromise resolves with failure', async () => { + await getCanAccessEmsFonts(); + expect(getGlyphs()).toEqual({ + isEmsFont: false, + glyphUrlTemplate: 'abc/api/maps/fonts/{fontstack}/{range}', + }); + }); + }); + + describe('online', () => { + beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('node-fetch').default = () => { + return Promise.resolve({ status: 200 }); + }; + }); + + test('should return EMS fonts template URL before canAccessEmsFontsPromise resolves', () => { + expect(getGlyphs()).toEqual({ + isEmsFont: true, + glyphUrlTemplate: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf', + }); + }); + + test('should return EMS fonts template URL after canAccessEmsFontsPromise resolves', async () => { + await getCanAccessEmsFonts(); + expect(getGlyphs()).toEqual({ + isEmsFont: true, + glyphUrlTemplate: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf', + }); + }); + }); +}); + +describe('EMS disabled', () => { + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../../kibana_services').getEMSSettings = () => { + return { + getEMSFontLibraryUrl: () => { + return 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; + }, + isEMSEnabled: () => false, + }; + }; + testOnlyClearCanAccessEmsFontsPromise(); + }); + + test('should return kibana fonts template URL before canAccessEmsFontsPromise resolves', () => { + expect(getGlyphs()).toEqual({ + isEmsFont: false, + glyphUrlTemplate: 'abc/api/maps/fonts/{fontstack}/{range}', + }); + }); + + test('should return kibana fonts template URL after canAccessEmsFontsPromise resolves', async () => { + await getCanAccessEmsFonts(); + expect(getGlyphs()).toEqual({ + isEmsFont: false, + glyphUrlTemplate: 'abc/api/maps/fonts/{fontstack}/{range}', + }); + }); +}); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.ts b/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.ts new file mode 100644 index 00000000000000..ec2d05e2994d20 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/mb_map/glyphs.ts @@ -0,0 +1,79 @@ +/* + * 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 fetch from 'node-fetch'; +import { FONTS_API_PATH } from '../../../common/constants'; +import { getDocLinks, getHttp, getEMSSettings } from '../../kibana_services'; + +let canAccessEmsFonts: boolean | undefined; +let canAccessEmsFontsPromise: Promise | null = null; +export async function getCanAccessEmsFonts(): Promise { + if (!canAccessEmsFontsPromise) { + canAccessEmsFontsPromise = new Promise(async (resolve) => { + try { + canAccessEmsFonts = undefined; + + const emsSettings = getEMSSettings(); + if (!emsSettings || !emsSettings.isEMSEnabled()) { + resolve(false); + } + const emsFontUrlTemplate = emsSettings.getEMSFontLibraryUrl(); + + const emsFontUrl = emsFontUrlTemplate + .replace('{fontstack}', 'Open Sans') + .replace('{range}', '0-255'); + const resp = await fetch(emsFontUrl, { + method: 'HEAD', + }); + if (resp.status >= 400) { + throw new Error(`status: ${resp.status}`); + } + canAccessEmsFonts = true; + resolve(true); + } catch (error) { + // eslint-disable-next-line no-console + console.warn( + `Unable to access fonts from Elastic Maps Service (EMS). To avoid unnecessary EMS requests, set 'map.includeElasticMapsService: false' in 'kibana.yml'. For more details please visit: ${ + getDocLinks().links.maps.connectToEms + }` + ); + canAccessEmsFonts = false; + resolve(false); + } + }); + } + return canAccessEmsFontsPromise; +} + +// test only function to reset singleton for different test cases. +export function testOnlyClearCanAccessEmsFontsPromise() { + canAccessEmsFontsPromise = null; + canAccessEmsFonts = undefined; +} + +export function getKibanaFontsGlyphUrl(): string { + return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); +} + +export function getGlyphs(): { glyphUrlTemplate: string; isEmsFont: boolean } { + const emsSettings = getEMSSettings(); + if ( + !emsSettings || + !emsSettings.isEMSEnabled() || + (typeof canAccessEmsFonts === 'boolean' && !canAccessEmsFonts) + ) { + return { + glyphUrlTemplate: getKibanaFontsGlyphUrl(), + isEmsFont: false, + }; + } + + return { + glyphUrlTemplate: emsSettings.getEMSFontLibraryUrl(), + isEmsFont: true, + }; +} diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index ab98b69a79f0bf..da4765ca094ecc 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -40,7 +40,7 @@ import { RawValue, ZOOM_PRECISION, } from '../../../common/constants'; -import { getGlyphUrl } from '../../util'; +import { getCanAccessEmsFonts, getGlyphs, getKibanaFontsGlyphUrl } from './glyphs'; import { syncLayerOrder } from './sort_layers'; import { removeOrphanedSourcesAndLayers } from './utils'; @@ -155,13 +155,13 @@ export class MbMap extends Component { async _createMbMapInstance(initialView: MapCenterAndZoom | null): Promise { this._reportUsage(); - const glyphsUrlTemplate = await getGlyphUrl(); return new Promise((resolve) => { + const glyphs = getGlyphs(); const mbStyle = { version: 8 as 8, sources: {}, layers: [], - glyphs: glyphsUrlTemplate, + glyphs: glyphs.glyphUrlTemplate, }; const options: MapOptions = { @@ -200,6 +200,20 @@ export class MbMap extends Component { emptyImage.crossOrigin = 'anonymous'; resolve(mbMap); }); + + if (glyphs.isEmsFont) { + getCanAccessEmsFonts().then((canAccessEmsFonts: boolean) => { + if (!this._isMounted || canAccessEmsFonts) { + return; + } + + // fallback to kibana fonts when EMS fonts are not accessable to prevent layers from not displaying + mbMap.setStyle({ + ...mbMap.getStyle(), + glyphs: getKibanaFontsGlyphUrl(), + }); + }); + } }); } diff --git a/x-pack/plugins/maps/public/util.test.js b/x-pack/plugins/maps/public/util.test.js index e86554560dd835..f07d60fc3d702e 100644 --- a/x-pack/plugins/maps/public/util.test.js +++ b/x-pack/plugins/maps/public/util.test.js @@ -5,103 +5,7 @@ * 2.0. */ -import { - getGlyphUrl, - makePublicExecutionContext, - testOnlyClearCanAccessEmsFontsPromise, -} from './util'; - -jest.mock('./kibana_services', () => ({ - getDocLinks: () => { - return { - links: { - maps: { - connectToEms: 'https://www.elastic.co/guide/en/kibana/current/maps-connect-to-ems.html', - }, - }, - }; - }, -})); - -describe('getGlyphUrl', () => { - describe('EMS enabled', () => { - beforeEach(() => { - require('./kibana_services').getHttp = () => ({ - basePath: { - prepend: (path) => `abc${path}`, - }, - }); - testOnlyClearCanAccessEmsFontsPromise(); - }); - - describe('offline', () => { - beforeAll(() => { - require('./kibana_services').getEMSSettings = () => { - return { - getEMSFontLibraryUrl() { - return 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; - }, - isEMSEnabled() { - return true; - }, - }; - }; - require('node-fetch').default = () => { - throw new Error('Simulated offline environment with no EMS access'); - }; - }); - - test('should return kibana fonts template URL', async () => { - expect(await getGlyphUrl()).toBe('abc/api/maps/fonts/{fontstack}/{range}'); - }); - }); - - describe('online', () => { - beforeAll(() => { - require('./kibana_services').getEMSSettings = () => { - return { - getEMSFontLibraryUrl() { - return 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf'; - }, - isEMSEnabled() { - return true; - }, - }; - }; - require('node-fetch').default = () => { - return Promise.resolve({ status: 200 }); - }; - }); - - test('should return EMS fonts template URL', async () => { - expect(await getGlyphUrl()).toBe( - 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf' - ); - }); - }); - }); - - describe('EMS disabled', () => { - beforeAll(() => { - require('./kibana_services').getHttp = () => { - return { - basePath: { - prepend: (path) => `abc${path}`, - }, - }; - }; - require('./kibana_services').getEMSSettings = () => { - return { - isEMSEnabled: () => false, - }; - }; - }); - - test('should return kibana fonts template URL', async () => { - expect(await getGlyphUrl()).toBe('abc/api/maps/fonts/{fontstack}/{range}'); - }); - }); -}); +import { makePublicExecutionContext } from './util'; describe('makePublicExecutionContext', () => { let injectedContext = {}; diff --git a/x-pack/plugins/maps/public/util.ts b/x-pack/plugins/maps/public/util.ts index 2a2e750941f306..68a616f8700000 100644 --- a/x-pack/plugins/maps/public/util.ts +++ b/x-pack/plugins/maps/public/util.ts @@ -5,13 +5,9 @@ * 2.0. */ -import fetch from 'node-fetch'; import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client'; import type { KibanaExecutionContext } from '@kbn/core/public'; -import { FONTS_API_PATH } from '../common/constants'; import { - getDocLinks, - getHttp, getTilemap, getEMSSettings, getMapsEmsStart, @@ -62,54 +58,6 @@ async function getEMSClient(): Promise { return emsClient; } -let canAccessEmsFontsPromise: Promise | null = null; -async function canAccessEmsFonts(): Promise { - if (!canAccessEmsFontsPromise) { - canAccessEmsFontsPromise = new Promise(async (resolve) => { - try { - const emsSettings = getEMSSettings(); - if (!emsSettings!.isEMSEnabled()) { - resolve(false); - } - const emsFontUrlTemplate = emsSettings!.getEMSFontLibraryUrl(); - - const emsFontUrl = emsFontUrlTemplate - .replace('{fontstack}', 'Open Sans') - .replace('{range}', '0-255'); - const resp = await fetch(emsFontUrl, { - method: 'HEAD', - }); - if (resp.status >= 400) { - throw new Error(`status: ${resp.status}`); - } - resolve(true); - } catch (error) { - // eslint-disable-next-line no-console - console.warn( - `Unable to access fonts from Elastic Maps Service (EMS). To avoid unnecessary EMS requests, set 'map.includeElasticMapsService: false' in 'kibana.yml'. For more details please visit: ${ - getDocLinks().links.maps.connectToEms - }` - ); - resolve(false); - } - }); - } - return canAccessEmsFontsPromise; -} -// test only function to reset singleton for different test cases. -export function testOnlyClearCanAccessEmsFontsPromise() { - canAccessEmsFontsPromise = null; -} - -export async function getGlyphUrl(): Promise { - const emsSettings = getEMSSettings(); - if (!emsSettings!.isEMSEnabled() || !(await canAccessEmsFonts())) { - return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); - } - - return emsSettings!.getEMSFontLibraryUrl(); -} - export function isRetina(): boolean { return window.devicePixelRatio === 2; } diff --git a/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts index 9a65bd0f2f95f6..949aaa65008c5b 100644 --- a/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts +++ b/x-pack/test/functional/apps/dashboard/group2/dashboard_maps_by_value.ts @@ -76,8 +76,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await PageObjects.dashboard.clickNewDashboard(); } - // Failing: See https://github.com/elastic/kibana/issues/152476 - describe.skip('dashboard maps by value', function () { + describe('dashboard maps by value', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); await kibanaServer.importExport.load(