Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions x-pack/plugins/maps/public/connected_components/mb_map/glyphs.test.ts
Original file line number Diff line number Diff line change
@@ -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}',
});
});
});
79 changes: 79 additions & 0 deletions x-pack/plugins/maps/public/connected_components/mb_map/glyphs.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> | null = null;
export async function getCanAccessEmsFonts(): Promise<boolean> {
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,
};
}
20 changes: 17 additions & 3 deletions x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -155,13 +155,13 @@ export class MbMap extends Component<Props, State> {

async _createMbMapInstance(initialView: MapCenterAndZoom | null): Promise<MapboxMap> {
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 = {
Expand Down Expand Up @@ -200,6 +200,20 @@ export class MbMap extends Component<Props, State> {
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(),
});
});
}
});
}

Expand Down
98 changes: 1 addition & 97 deletions x-pack/plugins/maps/public/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand Down
Loading