UNPKG

@elastic/ems-client

Version:

JavaScript client library for the Elastic Maps Service

327 lines (319 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LATEST_API_URL_PATH = exports.EMSClient = void 0; var _lodash = _interopRequireDefault(require("lodash")); var _tms_service = require("./tms_service"); var _file_layer = require("./file_layer"); var _coerce = _interopRequireDefault(require("semver/functions/coerce")); var _valid = _interopRequireDefault(require("semver/functions/valid")); var _major = _interopRequireDefault(require("semver/functions/major")); var _minor = _interopRequireDefault(require("semver/functions/minor")); var _url = require("url"); var _utils = require("./utils"); var _lruCache = require("lru-cache"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* * 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. */ const REST_API_REGEX = /\d{4}-\d{2}-\d{2}/; const LATEST_API_URL_PATH = exports.LATEST_API_URL_PATH = 'latest'; /** * plugins cannot have upstream dependencies on core/*-kibana. * Work-around by copy-pasting modifyUrl routine here. * @param url * @param block */ function modifyUrlLocal(url, block) { const parsed = (0, _url.parse)(url, true); // copy over the most specific version of each // property. By default, the parsed url includes // several conflicting properties (like path and // pathname + search, or search and query) and keeping // track of which property is actually used when they // are formatted is harder than necessary const meaningfulParts = { protocol: parsed.protocol, slashes: parsed.slashes, auth: parsed.auth, hostname: parsed.hostname, port: parsed.port, pathname: parsed.pathname, query: parsed.query || {}, hash: parsed.hash }; // the block modifies the meaningfulParts object, or returns a new one const modifiedParts = block(meaningfulParts) || meaningfulParts; // format the modified/replaced meaningfulParts back into a url return (0, _url.format)({ protocol: modifiedParts.protocol, slashes: modifiedParts.slashes, auth: modifiedParts.auth, hostname: modifiedParts.hostname, port: modifiedParts.port, pathname: modifiedParts.pathname, query: modifiedParts.query, hash: modifiedParts.hash }); } const extendUrl = (url, props) => modifyUrlLocal(url, parsed => _lodash.default.merge(parsed, props)); /** * Unescape a url template that was escaped by encodeURI() so leaflet * will be able to correctly locate the variables in the template * @param {String} url * @return {String} */ const unescapeTemplateVars = url => { const ENCODED_TEMPLATE_VARS_RE = /%7B(\w+?)%7D/g; return url.replace(ENCODED_TEMPLATE_VARS_RE, (total, varName) => `{${varName}}`); }; //this is not the default locale from Kibana, but the default locale supported by the Elastic Maps Service const DEFAULT_LANGUAGE = 'en'; class EMSClient { constructor(config) { _defineProperty(this, "EMS_LOAD_TIMEOUT", 32000); _defineProperty(this, "_queryParams", void 0); _defineProperty(this, "_appVersion", void 0); _defineProperty(this, "_fetchFunction", void 0); _defineProperty(this, "_sanitizer", void 0); _defineProperty(this, "_fileApiUrl", void 0); _defineProperty(this, "_tileApiUrl", void 0); _defineProperty(this, "_emsVersion", void 0); _defineProperty(this, "_emsLandingPageUrl", void 0); _defineProperty(this, "_language", void 0); _defineProperty(this, "_proxyPath", void 0); _defineProperty(this, "_cache", void 0); _defineProperty(this, "_isRestApi", false); /** * these methods are assigned outside the constructor */ _defineProperty(this, "_getMainCatalog", void 0); _defineProperty(this, "_getDefaultTMSCatalog", void 0); _defineProperty(this, "_getDefaultFileCatalog", void 0); _defineProperty(this, "_loadTMSServices", void 0); _defineProperty(this, "_loadFileLayers", void 0); // Remove kbnVersion in 8.0 if ('kbnVersion' in config) { console.warn('The "kbnVersion" parameter for ems-client is deprecated. Please use "appVersion" instead.'); this._appVersion = config.kbnVersion; } else { this._appVersion = config.appVersion; } this._queryParams = { elastic_tile_service_tos: 'agree', my_app_name: config.appName || 'kibana', my_app_version: this._appVersion }; this._sanitizer = config.htmlSanitizer ? config.htmlSanitizer : x => x; this._tileApiUrl = config.tileApiUrl; this._fileApiUrl = config.fileApiUrl; this._emsVersion = this._getEmsVersion(config.emsVersion); this._isRestApi = this._emsVersion == config.emsVersion; this._emsLandingPageUrl = config.landingPageUrl || ''; this._language = config.language || DEFAULT_LANGUAGE; this._fetchFunction = config.fetchFunction; this._proxyPath = config.proxyPath || ''; this._cache = new _lruCache.LRUCache({ max: config.cacheSize || 10 }); this._invalidateSettings(); } getDefaultLocale() { return DEFAULT_LANGUAGE; } getLocale() { return this._language; } getValueInLanguage(i18nObject) { if (!i18nObject) { return ''; } return i18nObject[this._language] ? i18nObject[this._language] : i18nObject[DEFAULT_LANGUAGE]; } /** * this internal method is overridden by the tests to simulate custom manifest. */ async getManifest(endpointUrl) { try { const url = extendUrl(endpointUrl, { query: this._queryParams }); const response = await this._fetchWithTimeout(url); if (!response.ok) { throw new Error(response.statusText); } if (response) { return await response.json(); } else { throw new Error('Response not found'); } } catch (e) { if (e instanceof Error) { throw e; } else { throw new Error('Unknown error', { cause: e }); } } } /** * Add optional query-parameters to all requests * * @param additionalQueryParams */ addQueryParams(additionalQueryParams) { for (const key in additionalQueryParams) { if (Object.prototype.hasOwnProperty.call(additionalQueryParams, key)) { if (additionalQueryParams[key] !== this._queryParams[key]) { //changes detected. this._queryParams = _lodash.default.assign({}, this._queryParams, additionalQueryParams); this._invalidateSettings(); break; } } } } async getMainManifest() { return await this._getMainCatalog(); } async getDefaultFileManifest() { return await this._getDefaultFileCatalog(); } async getDefaultTMSManifest() { return await this._getDefaultTMSCatalog(); } async getFileLayers() { return await this._loadFileLayers(); } async getTMSServices() { return await this._loadTMSServices(); } cacheGeoJson(layerId, geoJson) { this._cache.set(layerId, geoJson); return; } getCachedGeoJson(layerId) { return this._cache.get(layerId); } getEmsVersion() { return this._emsVersion; } getTileApiUrl() { return this._tileApiUrl; } getFileApiUrl() { return this._fileApiUrl; } getLandingPageUrl() { return this._emsLandingPageUrl; } sanitizeHtml(html) { return this._sanitizer(html); } extendUrlWithParams(url) { return unescapeTemplateVars(extendUrl(url, { query: this._queryParams })); } async findFileLayerById(id) { const fileLayers = await this.getFileLayers(); for (let i = 0; i < fileLayers.length; i++) { if (fileLayers[i].hasId(id)) { return fileLayers[i]; } } } async findTMSServiceById(id) { const tmsServices = await this.getTMSServices(); for (let i = 0; i < tmsServices.length; i++) { if (tmsServices[i].hasId(id)) { return tmsServices[i]; } } } _getEmsVersion(version) { if (version?.match(REST_API_REGEX)) { return version; } const semverVersion = (0, _valid.default)((0, _coerce.default)(version)); if (semverVersion) { return `v${(0, _major.default)(semverVersion)}.${(0, _minor.default)(semverVersion)}`; } else { throw new Error(`Invalid version: ${version}`); } } _fetchWithTimeout(url) { return new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(`Request to ${url} timed out`)), this.EMS_LOAD_TIMEOUT); this._fetchFunction(url).then(response => { clearTimeout(timer); resolve(response); }, err => { clearTimeout(timer); reject(err); }); }); } _invalidateSettings() { this._cache.clear(); this._getMainCatalog = _lodash.default.once(async () => { const services = []; if (this._tileApiUrl) { const version = this._isRestApi ? LATEST_API_URL_PATH : this._emsVersion; services.push({ type: 'tms', manifest: (0, _utils.toAbsoluteUrl)(this._tileApiUrl, `${version}/manifest`) }); } if (this._fileApiUrl) { const version = this._isRestApi ? LATEST_API_URL_PATH : this._emsVersion; services.push({ type: 'file', manifest: (0, _utils.toAbsoluteUrl)(this._fileApiUrl, `${version}/manifest`) }); } return { services: services }; }); this._getDefaultTMSCatalog = _lodash.default.once(async () => { const catalogue = await this._getMainCatalog(); const firstService = catalogue.services.find(service => service.type === 'tms'); if (!firstService) { return { services: [] }; } const url = this._proxyPath + firstService.manifest; return await this.getManifest(url); }); this._getDefaultFileCatalog = _lodash.default.once(async () => { const catalogue = await this._getMainCatalog(); const firstService = catalogue.services.find(service => service.type === 'file'); if (!firstService) { return { layers: [] }; } const url = this._proxyPath + firstService.manifest; return await this.getManifest(url); }); //Cache the actual instances of TMSService as these in turn cache sub-manifests for the style-files this._loadTMSServices = _lodash.default.once(async () => { const tmsManifest = await this._getDefaultTMSCatalog(); return tmsManifest.services.map(serviceConfig => new _tms_service.TMSService(serviceConfig, this, this._proxyPath)); }); this._loadFileLayers = _lodash.default.once(async () => { const fileManifest = await this._getDefaultFileCatalog(); return fileManifest.layers.map(layerConfig => new _file_layer.FileLayer(layerConfig, this, this._proxyPath)); }); } } exports.EMSClient = EMSClient;