[Maps] add ability to load EMS resources with CORS (#34503) (#36794)

- breaking change: by default, the Maps app now connects with CORS to EMS. This is optional
- no longer include the kbn-version header when requesting files over CORS in region maps
This commit is contained in:
Thomas Neirynck 2019-05-21 22:32:38 -04:00 committed by GitHub
parent aed5f6070f
commit 86d9001e95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 422 additions and 151 deletions

View file

@ -209,6 +209,7 @@ export default () => Joi.object({
}).default(),
map: Joi.object({
includeElasticMapsService: Joi.boolean().default(true),
proxyElasticMapsServiceInMaps: Joi.boolean().default(false),
tilemap: Joi.object({
url: Joi.string(),
options: Joi.object({

View file

@ -18,12 +18,7 @@
*/
import expect from '@kbn/expect';
import EMS_CATALOGUE from './ems_mocks/sample_manifest.json';
import EMS_FILES from './ems_mocks/sample_files.json';
import EMS_TILES from './ems_mocks/sample_tiles.json';
import { EMSClient } from '../../../../../core_plugins/tile_map/common/ems_client';
import { getEMSClient } from './ems_client_util';
describe('ems_client', () => {
@ -178,27 +173,3 @@ describe('ems_client', () => {
});
function getEMSClient(options = {}) {
const emsClient = new EMSClient({
language: 'en',
kbnVersion: '7.x.x',
manifestServiceUrl: 'https://foobar',
htmlSanitizer: x => x,
landingPageUrl: 'https://landing.foobar',
...options
});
emsClient._getManifest = async (url) => {
//simulate network calls
if (url.startsWith('https://foobar')) {
return EMS_CATALOGUE;
} else if (url.startsWith('https://tiles.foobar')) {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
}
};
return emsClient;
}

View file

@ -0,0 +1,49 @@
/*
* 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.
*/
import { EMSClient } from '../../../../../core_plugins/tile_map/common/ems_client';
import EMS_CATALOGUE from './ems_mocks/sample_manifest.json';
import EMS_FILES from './ems_mocks/sample_files.json';
import EMS_TILES from './ems_mocks/sample_tiles.json';
export function getEMSClient(options = {}) {
const emsClient = new EMSClient({
language: 'en',
kbnVersion: '7.x.x',
manifestServiceUrl: 'https://foobar',
htmlSanitizer: x => x,
landingPageUrl: 'https://landing.foobar',
...options
});
emsClient._getManifest = async (url) => {
//simulate network calls
if (url.startsWith('https://foobar')) {
return EMS_CATALOGUE;
} else if (url.startsWith('https://tiles.foobar')) {
return EMS_TILES;
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
}
};
return emsClient;
}

View file

@ -0,0 +1,21 @@
/*
* 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 { EMSClient } from '../../../../core_plugins/tile_map/common/ems_client';

View file

@ -32,7 +32,7 @@ const markdownIt = new MarkdownIt({
const TMS_IN_YML_ID = 'TMS in config/kibana.yml';
uiModules.get('kibana')
.service('serviceSettings', function ($http, $sanitize, mapConfig, tilemapsConfig, kbnVersion) {
.service('serviceSettings', function ($sanitize, mapConfig, tilemapsConfig, kbnVersion) {
const attributionFromConfig = $sanitize(markdownIt.render(tilemapsConfig.deprecated.config.options.attribution || ''));
const tmsOptionsFromConfig = _.assign({}, tilemapsConfig.deprecated.config.options, { attribution: attributionFromConfig });
@ -215,11 +215,8 @@ uiModules.get('kibana')
async getJsonForRegionLayer(fileLayerConfig) {
const url = await this.getUrlForRegionLayer(fileLayerConfig);
const json = await $http({
url: url,
method: 'GET'
});
return json.data;
const response = await fetch(url);
return await response.json();
}
}

View file

@ -5,8 +5,14 @@
*/
export const GIS_API_PATH = 'api/maps';
export const EMS_DATA_FILE_PATH = 'ems/file';
export const EMS_DATA_TMS_PATH = 'ems/tms';
export const EMS_META_PATH = 'ems/meta';
export const MAP_SAVED_OBJECT_TYPE = 'map';
export const APP_ID = 'maps';
export const APP_ICON = 'gisApp';
export function createMapPath(id) {
return `/app/maps#/map/${id}`;
}
@ -14,19 +20,13 @@ export function createMapPath(id) {
export const EMS_FILE = 'EMS_FILE';
export const ES_GEO_GRID = 'ES_GEO_GRID';
export const ES_SEARCH = 'ES_SEARCH';
export const SOURCE_DATA_ID_ORIGIN = 'source';
export const DECIMAL_DEGREES_PRECISION = 5; // meters precision
export const ZOOM_PRECISION = 2;
export const DEFAULT_EMS_TILE_LAYER = 'road_map';
export const APP_ID = 'maps';
export const APP_ICON = 'gisApp';
export const SOURCE_DATA_ID_ORIGIN = 'source';
export const FEATURE_ID_PROPERTY_NAME = '__kbn__feature_id__';
export const ES_GEO_FIELD_TYPE = {

View file

@ -0,0 +1,58 @@
/*
* 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 { EMS_DATA_FILE_PATH, EMS_DATA_TMS_PATH, GIS_API_PATH } from './constants';
export async function getEMSResources(emsClient, includeElasticMapsService, licenseUid, useRelativePathToProxy) {
if (!includeElasticMapsService) {
return {
fileLayers: [],
tmsServices: []
};
}
emsClient.addQueryParams({ license: licenseUid });
const fileLayerObjs = await emsClient.getFileLayers();
const tmsServicesObjs = await emsClient.getTMSServices();
const fileLayers = fileLayerObjs.map(fileLayer => {
//backfill to static settings
const format = fileLayer.getDefaultFormatType();
const meta = fileLayer.getDefaultFormatMeta();
return {
name: fileLayer.getDisplayName(),
origin: fileLayer.getOrigin(),
id: fileLayer.getId(),
created_at: fileLayer.getCreatedAt(),
attribution: fileLayer.getHTMLAttribution(),
attributions: fileLayer.getAttributions(),
fields: fileLayer.getFieldsInLanguage(),
// eslint-disable-next-line max-len
url: useRelativePathToProxy ? `../${GIS_API_PATH}/${EMS_DATA_FILE_PATH}?id=${encodeURIComponent(fileLayer.getId())}` : fileLayer.getDefaultFormatUrl(),
format: format, //legacy: format and meta are split up
meta: meta, //legacy, format and meta are split up,
emsLink: fileLayer.getEMSHotLink()
};
});
const tmsServices = tmsServicesObjs.map(tmsService => {
return {
origin: tmsService.getOrigin(),
id: tmsService.getId(),
minZoom: tmsService.getMinZoom(),
maxZoom: tmsService.getMaxZoom(),
attribution: tmsService.getHTMLAttribution(),
attributionMarkdown: tmsService.getMarkdownAttribution(),
// eslint-disable-next-line max-len
url: useRelativePathToProxy ? `../${GIS_API_PATH}/${EMS_DATA_TMS_PATH}?id=${encodeURIComponent(tmsService.getId())}&x={x}&y={y}&z={z}` : tmsService.getUrlTemplate()
};
});
return { fileLayers, tmsServices };
}

View file

@ -0,0 +1,60 @@
/*
* 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 {
getEMSResources
} from './ems_util';
// eslint-disable-next-line import/no-unresolved
import { getEMSClient } from 'ui/vis/__tests__/map/ems_client_util.js';
describe('ems util test', () => {
it('Should get relative paths when using proxy', async () => {
const emsClient = getEMSClient({});
const isEmsEnabled = true;
const licenseId = 'foobar';
const useProxy = true;
const resources = await getEMSResources(emsClient, isEmsEnabled, licenseId, useProxy);
expect(resources.tmsServices[0].url.startsWith('../api/maps/ems/tms')).toBe(true);
expect(resources.fileLayers[0].url.startsWith('../api/maps/ems/file')).toBe(true);
expect(resources.fileLayers[1].url.startsWith('../api/maps/ems/file')).toBe(true);
});
it('Should get absolute paths when not using proxy', async () => {
const emsClient = getEMSClient({});
const isEmsEnabled = true;
const licenseId = 'foobar';
const useProxy = false;
const resources = await getEMSResources(emsClient, isEmsEnabled, licenseId, useProxy);
expect(resources.tmsServices[0].url.startsWith('https://tiles-stage.elastic.co/')).toBe(true);
expect(resources.fileLayers[0].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
expect(resources.fileLayers[1].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
});
it('Should get empty response when ems is disabled', async () => {
const emsClient = getEMSClient({});
const isEmsEnabled = false;
const licenseId = 'foobar';
const useProxy = true;
const resources = await getEMSResources(emsClient, isEmsEnabled, licenseId, !useProxy);
expect(resources.tmsServices.length).toBe(0);
expect(resources.fileLayers.length).toBe(0);
});
});

View file

@ -18,6 +18,7 @@ import { initTelemetryCollection } from './server/maps_telemetry';
import { i18n } from '@kbn/i18n';
import { APP_ID, APP_ICON, createMapPath } from './common/constants';
import { getAppTitle } from './common/i18n_getters';
import _ from 'lodash';
export function maps(kibana) {
@ -39,9 +40,16 @@ export function maps(kibana) {
injectDefaultVars(server) {
const serverConfig = server.config();
const mapConfig = serverConfig.get('map');
return {
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
isEmsEnabled: mapConfig.includeElasticMapsService,
proxyElasticMapsServiceInMaps: mapConfig.proxyElasticMapsServiceInMaps,
emsManifestServiceUrl: mapConfig.manifestServiceUrl,
emsLandingPageUrl: mapConfig.manifestServiceUrl,
kbnPkgVersion: serverConfig.get('pkg.version'),
regionmapLayers: _.get(mapConfig, 'regionmap.layers', []),
tilemap: _.get(mapConfig, 'tilemap', [])
};
},
embeddableFactories: [

View file

@ -6,7 +6,7 @@
import _ from 'lodash';
import { KibanaTilemapSource } from '../shared/layers/sources/kibana_tilemap_source';
import { EMSTMSSource } from '../shared/layers/sources/ems_tms_source';
import { isMetaDataLoaded, getDataSourcesSync } from '../meta';
import { isMetaDataLoaded, getEMSDataSourcesSync, getKibanaTileMap } from '../meta';
import { DEFAULT_EMS_TILE_LAYER } from '../../common/constants';
export function getInitialLayers(savedMapLayerListJSON) {
@ -15,6 +15,16 @@ export function getInitialLayers(savedMapLayerListJSON) {
return JSON.parse(savedMapLayerListJSON);
}
const tilemapSourceFromKibana = getKibanaTileMap();
if (_.get(tilemapSourceFromKibana, 'url')) {
const sourceDescriptor = KibanaTilemapSource.createDescriptor();
const source = new KibanaTilemapSource(sourceDescriptor);
const layer = source.createDefaultLayer();
return [
layer.toLayerDescriptor()
];
}
if (!isMetaDataLoaded()) {
const descriptor = EMSTMSSource.createDescriptor(DEFAULT_EMS_TILE_LAYER);
const source = new EMSTMSSource(descriptor);
@ -24,17 +34,8 @@ export function getInitialLayers(savedMapLayerListJSON) {
];
}
const dataSources = getDataSourcesSync();
if (_.get(dataSources, 'kibana.tilemap.url')) {
const sourceDescriptor = KibanaTilemapSource.createDescriptor();
const source = new KibanaTilemapSource(sourceDescriptor);
const layer = source.createDefaultLayer();
return [
layer.toLayerDescriptor()
];
}
const emsTmsServices = _.get(dataSources, 'ems.tms');
const emsDataSources = getEMSDataSourcesSync();
const emsTmsServices = _.get(emsDataSources, 'ems.tms');
if (emsTmsServices && emsTmsServices.length > 0) {
const sourceDescriptor = EMSTMSSource.createDescriptor(emsTmsServices[0].id);
const source = new EMSTMSSource(sourceDescriptor, { emsTmsServices });

View file

@ -47,12 +47,15 @@ describe('Saved object does not have layer list', () => {
});
function mockDataSourceResponse(dataSources) {
require('../meta').getDataSourcesSync = () => {
require('../meta').getEMSDataSourcesSync = () => {
return dataSources;
};
require('../meta').isMetaDataLoaded = () => {
return true;
};
require('../meta').getKibanaTileMap = () => {
return null;
};
}
it('should get the default EMS layer when metadata has not loaded yet', () => {
@ -86,9 +89,11 @@ describe('Saved object does not have layer list', () => {
it('Should get initial layer from Kibana tilemap data source when Kibana tilemap is configured ', () => {
mockDataSourceResponse({
kibana: mockKibanaDataSource,
ems: mockEmsDataSource
});
require('../meta').getKibanaTileMap = () => {
return mockKibanaDataSource.tilemap;
};
const layers = getInitialLayers(null);
expect(layers).toEqual([{

View file

@ -8,10 +8,12 @@ import { uiModules } from 'ui/modules';
import { SearchSourceProvider } from 'ui/courier';
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
import { getRequestInspectorStats, getResponseInspectorStats } from 'ui/courier/utils/courier_inspector_utils';
import { XPackInfoProvider } from 'plugins/xpack_main/services/xpack_info';
export let indexPatternService;
export let SearchSource;
export let filterBarQueryFilter;
export let xpackInfo;
export async function fetchSearchSourceAndRecordWithInspector({ searchSource, requestId, requestName, requestDesc, inspectorAdapters }) {
const inspectorRequest = inspectorAdapters.requests.start(
@ -40,4 +42,5 @@ uiModules.get('app/maps').run(($injector) => {
const Private = $injector.get('Private');
SearchSource = Private(SearchSourceProvider);
filterBarQueryFilter = Private(FilterBarQueryFilterProvider);
xpackInfo = Private(XPackInfoProvider);
});

View file

@ -5,17 +5,23 @@
*/
import { GIS_API_PATH } from '../common/constants';
import { GIS_API_PATH, EMS_META_PATH } from '../common/constants';
import _ from 'lodash';
import { getEMSResources } from '../common/ems_util';
import chrome from 'ui/chrome';
import { i18n } from '@kbn/i18n';
import { EMSClient } from 'ui/vis/map/ems_client';
import { xpackInfo } from './kibana_services';
const GIS_API_RELATIVE = `../${GIS_API_PATH}`;
let meta = null;
let emsSources = null;
let loadingMetaPromise = null;
let isLoaded = false;
export async function getDataSources() {
if (meta) {
return meta;
export async function getEMSDataSources() {
if (emsSources) {
return emsSources;
}
if (loadingMetaPromise) {
@ -24,12 +30,32 @@ export async function getDataSources() {
loadingMetaPromise = new Promise(async (resolve, reject) => {
try {
const response = await fetch(`${GIS_API_RELATIVE}/meta`);
const metaJson = await response.json();
const proxyElasticMapsServiceInMaps = chrome.getInjected('proxyElasticMapsServiceInMaps', false);
if (proxyElasticMapsServiceInMaps) {
const fullResponse = await fetch(`${GIS_API_RELATIVE}/${EMS_META_PATH}`);
emsSources = await fullResponse.json();
} else {
const emsClient = new EMSClient({
language: i18n.getLocale(),
kbnVersion: chrome.getInjected('kbnPkgVersion'),
manifestServiceUrl: chrome.getInjected('emsManifestServiceUrl'),
landingPageUrl: chrome.getInjected('emsLandingPageUrl')
});
const isEmsEnabled = chrome.getInjected('isEmsEnabled', true);
const xpackMapsFeature = xpackInfo.get('features.maps');
const licenseId = xpackMapsFeature && xpackMapsFeature.maps && xpackMapsFeature.uid ? xpackMapsFeature.uid : '';
const emsResponse = await getEMSResources(emsClient, isEmsEnabled, licenseId, false);
emsSources = {
ems: {
file: emsResponse.fileLayers,
tms: emsResponse.tmsServices
}
};
}
isLoaded = true;
meta = metaJson.data_sources;
resolve(meta);
} catch(e) {
resolve(emsSources);
} catch (e) {
reject(e);
}
});
@ -39,8 +65,8 @@ export async function getDataSources() {
/**
* Should only call this after verifying `isMetadataLoaded` equals true
*/
export function getDataSourcesSync() {
return meta;
export function getEMSDataSourcesSync() {
return emsSources;
}
export function isMetaDataLoaded() {
@ -48,21 +74,19 @@ export function isMetaDataLoaded() {
}
export async function getEmsVectorFilesMeta() {
const dataSource = await getDataSources();
const dataSource = await getEMSDataSources();
return _.get(dataSource, 'ems.file', []);
}
export async function getEmsTMSServices() {
const dataSource = await getDataSources();
const dataSource = await getEMSDataSources();
return _.get(dataSource, 'ems.tms', []);
}
export async function getKibanaRegionList() {
const dataSource = await getDataSources();
return _.get(dataSource, 'kibana.regionmap', []);
export function getKibanaRegionList() {
return chrome.getInjected('regionmapLayers');
}
export async function getKibanaTileMap() {
const dataSource = await getDataSources();
return _.get(dataSource, 'kibana.tilemap', {});
export function getKibanaTileMap() {
return chrome.getInjected('tilemap');
}

View file

@ -0,0 +1,64 @@
/*
* 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 {
getEMSDataSources
} from './meta';
jest.mock('ui/chrome',
() => ({
getBasePath: () => {
return '<basepath>';
},
getInjected(key) {
if (key === 'proxyElasticMapsServiceInMaps') {
return false;
} else if (key === 'isEmsEnabled') {
return true;
}
},
getUiSettingsClient: () => {
return {
get: () => {
return '';
}
};
},
})
);
jest.mock('ui/vis/map/ems_client', () => {
const module = require('ui/vis/__tests__/map/ems_client_util.js');
function EMSClient() {
return module.getEMSClient();
}
return {
EMSClient: EMSClient
};
});
jest.mock('./kibana_services', () => {
return {
xpackInfo: {
get() {
return 'foobarlicenseid';
}
}
};
});
describe('default use without proxy', () => {
it('should return absolute urls', async () => {
const resources = await getEMSDataSources();
expect(resources.ems.tms[0].url.startsWith('https://tiles-stage.elastic.co/')).toBe(true);
expect(resources.ems.file[0].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
expect(resources.ems.file[1].url.startsWith('https://vector-staging.maps.elastic.co/files')).toBe(true);
});
});

View file

@ -7,7 +7,7 @@
import { AbstractVectorSource } from '../vector_source';
import { VECTOR_SHAPE_TYPES } from '../vector_feature_types';
import React from 'react';
import { GIS_API_PATH, EMS_FILE } from '../../../../../common/constants';
import { EMS_FILE } from '../../../../../common/constants';
import { getEmsVectorFilesMeta } from '../../../../meta';
import { EMSFileCreateSourceEditor } from './create_source_editor';
import { i18n } from '@kbn/i18n';
@ -59,7 +59,7 @@ export class EMSFileSource extends AbstractVectorSource {
const featureCollection = await AbstractVectorSource.getGeoJson({
format: emsVectorFileMeta.format,
featureCollectionPath: 'data',
fetchUrl: `../${GIS_API_PATH}/data/ems?id=${encodeURIComponent(this._descriptor.id)}`
fetchUrl: emsVectorFileMeta.url
});
return {
data: featureCollection,

View file

@ -20,7 +20,7 @@ export class CreateSourceEditor extends React.Component {
}
_loadList = async () => {
const list = await getKibanaRegionList();
const list = getKibanaRegionList();
if (this._isMounted) {
this.setState({
regionmapLayers: list

View file

@ -59,7 +59,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
}
async _getVectorFileMeta() {
const regionList = await getKibanaRegionList();
const regionList = getKibanaRegionList();
const meta = regionList.find(source => source.name === this._descriptor.name);
if (!meta) {
throw new Error(i18n.translate('xpack.maps.source.kbnRegionMap.noConfigErrorMessage', {

View file

@ -21,7 +21,7 @@ export class CreateSourceEditor extends Component {
};
_loadUrl = async () => {
const tilemap = await getKibanaTileMap();
const tilemap = getKibanaTileMap();
if (this._isMounted) {
this.setState(
{ url: tilemap.url },

View file

@ -68,7 +68,7 @@ export class KibanaTilemapSource extends AbstractTMSSource {
}
async getUrlTemplate() {
const tilemap = await getKibanaTileMap();
const tilemap = getKibanaTileMap();
if (!tilemap.url) {
throw new Error(i18n.translate('xpack.maps.source.kbnTMS.noConfigErrorMessage', {
defaultMessage: `Unable to find map.tilemap.url configuration in the kibana.yml`

View file

@ -5,10 +5,11 @@
*/
import { GIS_API_PATH } from '../common/constants';
import { EMS_DATA_FILE_PATH, EMS_DATA_TMS_PATH, EMS_META_PATH, GIS_API_PATH } from '../common/constants';
import fetch from 'node-fetch';
import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import { getEMSResources } from '../common/ems_util';
import Boom from 'boom';
const ROOT = `/${GIS_API_PATH}`;
@ -26,37 +27,99 @@ export function initRoutes(server, licenseUid) {
server.route({
method: 'GET',
path: `${ROOT}/data/ems`,
path: `${ROOT}/${EMS_DATA_FILE_PATH}`,
handler: async (request) => {
if (!mapConfig.proxyElasticMapsServiceInMaps) {
server.log('warning', `Cannot load content from EMS when map.proxyElasticMapsServiceInMaps is turned off`);
throw Boom.notFound();
}
if (!request.query.id) {
server.log('warning', 'Must supply id parameters to retrieve EMS file');
return null;
}
const ems = await getEMSResources(licenseUid);
const ems = await getEMSResources(emsClient, mapConfig.includeElasticMapsService, licenseUid, false);
const layer = ems.fileLayers.find(layer => layer.id === request.query.id);
if (!layer) {
return null;
}
const file = await fetch(layer.url);
return await file.json();
try {
const file = await fetch(layer.url);
return await file.json();
} catch(e) {
server.log('warning', `Cannot connect to EMS for file, error: ${e.message}`);
throw Boom.badRequest(`Cannot connect to EMS`);
}
}
});
server.route({
method: 'GET',
path: `${ROOT}/${EMS_DATA_TMS_PATH}`,
handler: async (request, h) => {
if (!mapConfig.proxyElasticMapsServiceInMaps) {
server.log('warning', `Cannot load content from EMS when map.proxyElasticMapsServiceInMaps is turned off`);
throw Boom.notFound();
}
if (!request.query.id ||
typeof parseInt(request.query.x, 10) !== 'number' ||
typeof parseInt(request.query.y, 10) !== 'number' ||
typeof parseInt(request.query.z, 10) !== 'number'
) {
server.log('warning', 'Must supply id/x/y/z parameters to retrieve EMS tile');
return null;
}
const ems = await getEMSResources(emsClient, mapConfig.includeElasticMapsService, licenseUid, false);
const tmsService = ems.tmsServices.find(layer => layer.id === request.query.id);
if (!tmsService) {
return null;
}
const url = tmsService.url
.replace('{x}', request.query.x)
.replace('{y}', request.query.y)
.replace('{z}', request.query.z);
try {
const tile = await fetch(url);
const arrayBuffer = await tile.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
let response = h.response(buffer);
response = response.bytes(buffer.length);
response = response.header('Content-Disposition', 'inline');
response = response.header('Content-type', 'image/png');
response = response.encoding('binary');
return response;
} catch(e) {
server.log('warning', `Cannot connect to EMS for tile, error: ${e.message}`);
throw Boom.badRequest(`Cannot connect to EMS`);
}
}
});
server.route({
method: 'GET',
path: `${ROOT}/meta`,
path: `${ROOT}/${EMS_META_PATH}`,
handler: async () => {
if (!mapConfig.proxyElasticMapsServiceInMaps) {
server.log('warning', `Cannot load content from EMS when map.proxyElasticMapsServiceInMaps is turned off`);
throw Boom.notFound();
}
let ems;
try {
ems = await getEMSResources(licenseUid);
ems = await getEMSResources(emsClient, mapConfig.includeElasticMapsService, licenseUid, true);
} catch (e) {
server.log('warning', `Cannot connect to EMS, error: ${e}`);
server.log('warning', `Cannot connect to EMS, error: ${e.message}`);
ems = {
fileLayers: [],
tmsServices: []
@ -64,15 +127,9 @@ export function initRoutes(server, licenseUid) {
}
return ({
data_sources: {
ems: {
file: ems.fileLayers,
tms: ems.tmsServices
},
kibana: {
regionmap: _.get(mapConfig, 'regionmap.layers', []),
tilemap: _.get(mapConfig, 'tilemap', [])
}
ems: {
file: ems.fileLayers,
tms: ems.tmsServices
}
});
}
@ -93,57 +150,9 @@ export function initRoutes(server, licenseUid) {
try {
const { count } = await callWithRequest(request, 'count', { index: query.index });
return { count };
} catch(error) {
} catch (error) {
return h.response().code(400);
}
}
});
async function getEMSResources(licenseUid) {
if (!mapConfig.includeElasticMapsService) {
return {
fileLayers: [],
tmsServices: []
};
}
emsClient.addQueryParams({ license: licenseUid });
const fileLayerObjs = await emsClient.getFileLayers();
const tmsServicesObjs = await emsClient.getTMSServices();
const fileLayers = fileLayerObjs.map(fileLayer => {
//backfill to static settings
const format = fileLayer.getDefaultFormatType();
const meta = fileLayer.getDefaultFormatMeta();
return {
name: fileLayer.getDisplayName(),
origin: fileLayer.getOrigin(),
id: fileLayer.getId(),
created_at: fileLayer.getCreatedAt(),
attribution: fileLayer.getHTMLAttribution(),
attributions: fileLayer.getAttributions(),
fields: fileLayer.getFieldsInLanguage(),
url: fileLayer.getDefaultFormatUrl(),
format: format, //legacy: format and meta are split up
meta: meta, //legacy, format and meta are split up,
emsLink: fileLayer.getEMSHotLink()
};
});
const tmsServices = tmsServicesObjs.map(tmsService => {
return {
origin: tmsService.getOrigin(),
id: tmsService.getId(),
minZoom: tmsService.getMinZoom(),
maxZoom: tmsService.getMaxZoom(),
attribution: tmsService.getHTMLAttribution(),
attributionMarkdown: tmsService.getMarkdownAttribution(),
url: tmsService.getUrlTemplate()
};
});
return { fileLayers, tmsServices };
}
}