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 639c8fe5a20db..ab98b69a79f0b 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 @@ -155,12 +155,13 @@ export class MbMap extends Component { async _createMbMapInstance(initialView: MapCenterAndZoom | null): Promise { this._reportUsage(); + const glyphsUrlTemplate = await getGlyphUrl(); return new Promise((resolve) => { const mbStyle = { version: 8 as 8, sources: {}, layers: [], - glyphs: getGlyphUrl(), + glyphs: glyphsUrlTemplate, }; const options: MapOptions = { diff --git a/x-pack/plugins/maps/public/util.test.js b/x-pack/plugins/maps/public/util.test.js index 7fc88578b378a..48498f87fe7c0 100644 --- a/x-pack/plugins/maps/public/util.test.js +++ b/x-pack/plugins/maps/public/util.test.js @@ -5,60 +5,88 @@ * 2.0. */ -import { getGlyphUrl, makePublicExecutionContext } from './util'; - -const MOCK_EMS_SETTINGS = { - isEMSEnabled: () => true, -}; +import { + getGlyphUrl, + makePublicExecutionContext, + testOnlyClearCanAccessEmsFontsPromise, +} from './util'; describe('getGlyphUrl', () => { describe('EMS enabled', () => { - beforeAll(() => { + beforeEach(() => { require('./kibana_services').getHttp = () => ({ basePath: { - prepend: (url) => url, // No need to actually prepend a dev basepath for test + prepend: (path) => `abc${path}`, }, }); + testOnlyClearCanAccessEmsFontsPromise(); }); - describe('EMS proxy disabled', () => { + describe('offline', () => { beforeAll(() => { require('./kibana_services').getEMSSettings = () => { return { getEMSFontLibraryUrl() { - return 'foobar'; + 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 EMS fonts URL', async () => { - expect(getGlyphUrl()).toBe('foobar'); + 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(() => { - const mockHttp = { - basePath: { - prepend: (path) => `abc${path}`, - }, + require('./kibana_services').getHttp = () => { + return { + basePath: { + prepend: (path) => `abc${path}`, + }, + }; }; - require('./kibana_services').getHttp = () => mockHttp; require('./kibana_services').getEMSSettings = () => { return { - ...MOCK_EMS_SETTINGS, isEMSEnabled: () => false, }; }; }); - test('should return kibana fonts URL', async () => { - expect(getGlyphUrl()).toBe('abc/api/maps/fonts/{fontstack}/{range}'); + test('should return kibana fonts template URL', async () => { + expect(await getGlyphUrl()).toBe('abc/api/maps/fonts/{fontstack}/{range}'); }); }); }); diff --git a/x-pack/plugins/maps/public/util.ts b/x-pack/plugins/maps/public/util.ts index d63072b163599..3243c8a95cf74 100644 --- a/x-pack/plugins/maps/public/util.ts +++ b/x-pack/plugins/maps/public/util.ts @@ -5,6 +5,7 @@ * 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'; @@ -60,9 +61,46 @@ async function getEMSClient(): Promise { return emsClient; } -export function getGlyphUrl(): string { +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). Set kibana.yml 'map.includeElasticMapsService: false' to avoid unnecessary EMS requests.` + ); + 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()) { + if (!emsSettings!.isEMSEnabled() || !(await canAccessEmsFonts())) { return getHttp().basePath.prepend(`/${FONTS_API_PATH}/{fontstack}/{range}`); }