[Maps][Vega] Enable on-prem for Vega (#104422)

This commit is contained in:
Thomas Neirynck 2022-01-12 08:52:45 -05:00 committed by GitHub
parent a9ec1be357
commit 00d5791775
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 780 additions and 12754 deletions

View file

@ -201,7 +201,7 @@ management section itself.
|{kib-repo}blob/{branch}/src/plugins/maps_ems/README.md[mapsEms]
|Configuration of kibana-wide EMS settings and some higher level utilities.
|Utility plugin:
|{kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation]

View file

@ -1,3 +1,6 @@
# Maps EMS
Configuration of kibana-wide EMS settings and some higher level utilities.
Utility plugin:
- Typing for the `map.*` yml configuration. (`MapConfig`).
- These settings includes EMS-related configuration (e.g. for on-prem EMS deployments)
- Higher level utilities to construct an EMS-client based on these configuration parameters

File diff suppressed because it is too large Load diff

View file

@ -1,16 +0,0 @@
{
"services": [
{
"id": "tiles_v2",
"name": "Elastic Maps Tile Service",
"manifest": "https://tiles.foobar/v7.6/manifest",
"type": "tms"
},
{
"id": "geo_layers",
"name": "Elastic Maps Vector Service",
"manifest": "https://files.foobar/v7.6/manifest",
"type": "file"
}
]
}

View file

@ -1,12 +0,0 @@
{
"tilejson": "2.0.0",
"name": "OSM Bright",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 10,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["/raster/styles/osm-bright/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -1,735 +0,0 @@
{
"tiles": [
"/data/v3/{z}/{x}/{y}.pbf"
],
"name": "OpenMapTiles",
"format": "pbf",
"basename": "planet.mbtiles",
"id": "openmaptiles",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"bounds": [
-180,
-85.0511,
180,
85.0511
],
"center": [
-12.2168,
28.6135,
4
],
"description": "A tileset showcasing all layers in OpenMapTiles. https://openmaptiles.org",
"maxzoom": 10,
"minzoom": 0,
"pixel_scale": "256",
"vector_layers": [
{
"maxzoom": 10,
"fields": {
"class": "String"
},
"minzoom": 0,
"id": "water",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"brunnel": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "waterway",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"class": "String",
"subclass": "String"
},
"minzoom": 0,
"id": "landcover",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"class": "String"
},
"minzoom": 0,
"id": "landuse",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"rank": "Number",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"osm_id": "Number",
"name:ml": "String",
"name:pl": "String",
"ele": "Number",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:ru": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"ele_ft": "Number",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "mountain_peak",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"rank": "Number",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:sr": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"class": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "park",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"admin_level": "Number",
"disputed": "Number",
"maritime": "Number"
},
"minzoom": 0,
"id": "boundary",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"ref": "String",
"class": "String"
},
"minzoom": 0,
"id": "aeroway",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"layer": "Number",
"service": "String",
"level": "Number",
"brunnel": "String",
"indoor": "Number",
"ramp": "Number",
"subclass": "String",
"oneway": "Number",
"class": "String"
},
"minzoom": 0,
"id": "transportation",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"render_min_height": "Number",
"hide_3d": "Boolean",
"render_height": "Number"
},
"minzoom": 0,
"id": "building",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "water_name",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"layer": "Number",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"subclass": "String",
"name:de": "String",
"indoor": "Number",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"network": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"ref": "String",
"name:ja_kana": "String",
"level": "Number",
"ref_length": "Number",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "transportation_name",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"rank": "Number",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"capital": "Number",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"iso_a2": "String",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "place",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"housenumber": "String"
},
"minzoom": 0,
"id": "housenumber",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"layer": "Number",
"rank": "Number",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"name:ru": "String",
"name:ml": "String",
"name:pl": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"subclass": "String",
"name:de": "String",
"indoor": "Number",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"name:uk": "String",
"name:hy": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"name:ja_kana": "String",
"level": "Number",
"name:is": "String",
"name:th": "String",
"agg_stop": "Number",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "poi",
"description": ""
},
{
"maxzoom": 10,
"fields": {
"name:mt": "String",
"name:pt": "String",
"name:az": "String",
"name:cy": "String",
"name:rm": "String",
"name:ko": "String",
"name:kn": "String",
"name:ar": "String",
"name:cs": "String",
"name_de": "String",
"name:ro": "String",
"name:it": "String",
"osm_id": "Number",
"name:ml": "String",
"name:pl": "String",
"ele": "Number",
"iata": "String",
"name:ca": "String",
"name_int": "String",
"name:hu": "String",
"name:ka": "String",
"name:fi": "String",
"name:da": "String",
"name:de": "String",
"name:tr": "String",
"name:fr": "String",
"name:mk": "String",
"name:sl": "String",
"name:nonlatin": "String",
"name:fy": "String",
"name:zh": "String",
"name:ko_rm": "String",
"name:lv": "String",
"name:ja": "String",
"name:lt": "String",
"name:no": "String",
"name:kk": "String",
"name:sv": "String",
"name:he": "String",
"name:ja_rm": "String",
"name:ga": "String",
"name:br": "String",
"name:bs": "String",
"name:lb": "String",
"class": "String",
"name:la": "String",
"name:sk": "String",
"ele_ft": "Number",
"name:uk": "String",
"name:hy": "String",
"name:ru": "String",
"name:be": "String",
"name_en": "String",
"name:bg": "String",
"name:hr": "String",
"name:sr": "String",
"name:sq": "String",
"name:el": "String",
"name:eo": "String",
"name:en": "String",
"name": "String",
"name:gd": "String",
"icao": "String",
"name:ja_kana": "String",
"name:is": "String",
"name:th": "String",
"name:latin": "String",
"name:sr-Latn": "String",
"name:et": "String",
"name:nl": "String",
"name:es": "String"
},
"minzoom": 0,
"id": "aerodrome_label",
"description": ""
}
],
"maskLevel": "8",
"planettime": "1555286400000",
"version": "3.9",
"tilejson": "2.0.0"
}

View file

@ -1,12 +0,0 @@
{
"tilejson": "2.0.0",
"name": "Dark Matter",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 22,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["/raster/styles/dark-matter/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -1,12 +0,0 @@
{
"tilejson": "2.0.0",
"name": "OSM Bright Desaturated",
"attribution": "<a href=\"https://www.maptiler.com/copyright/\" target=\"_blank\">&copy; MapTiler</a> <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">&copy; OpenStreetMap contributors</a>",
"minzoom": 0,
"maxzoom": 18,
"bounds": [-180, -85.0511, 180, 85.0511],
"format": "png",
"type": "baselayer",
"tiles": ["/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png"],
"center": [0, 0, 2]
}

View file

@ -1,88 +0,0 @@
{
"services": [
{
"id": "road_map",
"name": { "en": "Road Map - Bright" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "<iframe id='iframe' style='position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;' src='http://256.256.256.256'></iframe>" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "/v7.6/styles/osm-bright/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "/v7.6/styles/osm-bright.json"
}
]
},
{
"id": "road_map_desaturated",
"name": { "en": "Road Map" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "/v7.6/styles/osm-bright-desaturated/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "/v7.6/styles/osm-bright-desaturated.json"
}
]
},
{
"id": "dark_map",
"name": { "en": "Dark Map" },
"attribution": [
{
"label": { "en": "OpenStreetMap contributors" },
"url": { "en": "https://www.openstreetmap.org/copyright" }
},
{ "label": { "en": "OpenMapTiles" }, "url": { "en": "https://openmaptiles.org" } },
{ "label": { "en": "MapTiler" }, "url": { "en": "https://www.maptiler.com" } },
{
"label": { "en": "Elastic Maps Service" },
"url": { "en": "https://www.elastic.co/elastic-maps-service" }
}
],
"formats": [
{
"locale": "en",
"format": "vector",
"url": "/v7.6/styles/dark-matter/style.json"
},
{
"locale": "en",
"format": "raster",
"url": "/v7.6/styles/dark-matter.json"
}
]
}
]
}

View file

@ -0,0 +1,19 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export const DEFAULT_EMS_FILE_API_URL = 'https://vector.maps.elastic.co';
export const DEFAULT_EMS_TILE_API_URL = 'https://tiles.maps.elastic.co';
export const DEFAULT_EMS_LANDING_PAGE_URL = 'https://maps.elastic.co/v8.0';
export const DEFAULT_EMS_FONT_LIBRARY_URL =
'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf';
export const DEFAULT_EMS_ROADMAP_ID = 'road_map';
export const DEFAULT_EMS_ROADMAP_DESATURATED_ID = 'road_map_desaturated';
export const DEFAULT_EMS_DARKMAP_ID = 'dark_map';
export const EMS_APP_NAME = 'kibana'; // app-name submitted as the `app`-param to EMS

View file

@ -1,29 +1,30 @@
/*
* 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.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { EMSSettings, IEMSConfig } from './ems_settings';
import { EMSSettings } from './ems_settings';
import {
DEFAULT_EMS_FILE_API_URL,
DEFAULT_EMS_FONT_LIBRARY_URL,
DEFAULT_EMS_LANDING_PAGE_URL,
DEFAULT_EMS_TILE_API_URL,
} from '../../../../src/plugins/maps_ems/common';
} from './ems_defaults';
import type { EMSConfig } from './ems_settings';
const IS_ENTERPRISE_PLUS = () => true;
describe('EMSSettings', () => {
const mockConfig: IEMSConfig = {
const mockConfig: EMSConfig = {
includeElasticMapsService: true,
emsUrl: '',
emsFileApiUrl: DEFAULT_EMS_FILE_API_URL,
emsTileApiUrl: DEFAULT_EMS_TILE_API_URL,
emsLandingPageUrl: DEFAULT_EMS_LANDING_PAGE_URL,
emsFontLibraryUrl: DEFAULT_EMS_FONT_LIBRARY_URL,
isEMSEnabled: true,
};
describe('isEMSEnabled/isOnPrem', () => {

View file

@ -1,8 +1,9 @@
/*
* 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.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import {
@ -10,25 +11,28 @@ import {
DEFAULT_EMS_FONT_LIBRARY_URL,
DEFAULT_EMS_LANDING_PAGE_URL,
DEFAULT_EMS_TILE_API_URL,
} from '../../../../src/plugins/maps_ems/common';
} from './ems_defaults';
export interface IEMSConfig {
emsUrl?: string;
includeElasticMapsService?: boolean;
emsFileApiUrl?: string;
emsTileApiUrl?: string;
emsLandingPageUrl?: string;
emsFontLibraryUrl?: string;
isEMSEnabled?: boolean;
export interface EMSConfig {
includeElasticMapsService: boolean;
emsUrl: string;
emsFileApiUrl: string;
emsTileApiUrl: string;
emsLandingPageUrl: string;
emsFontLibraryUrl: string;
}
export function createEMSSettings(emsConfig: EMSConfig, getIsEnterprisePlus: () => boolean) {
return new EMSSettings(emsConfig, getIsEnterprisePlus);
}
export class EMSSettings {
private readonly _config: IEMSConfig;
private readonly _config: EMSConfig;
private readonly _getIsEnterprisePlus: () => boolean;
constructor(config: IEMSConfig, getIsEnterPrisePlus: () => boolean) {
constructor(config: EMSConfig, getIsEnterprisePlus: () => boolean) {
this._config = config;
this._getIsEnterprisePlus = getIsEnterPrisePlus;
this._getIsEnterprisePlus = getIsEnterprisePlus;
}
isEMSUrlSet() {

View file

@ -6,16 +6,18 @@
* Side Public License, v 1.
*/
export const TMS_IN_YML_ID = 'TMS in config/kibana.yml';
export {
DEFAULT_EMS_FILE_API_URL,
DEFAULT_EMS_TILE_API_URL,
DEFAULT_EMS_LANDING_PAGE_URL,
DEFAULT_EMS_FONT_LIBRARY_URL,
DEFAULT_EMS_ROADMAP_ID,
DEFAULT_EMS_ROADMAP_DESATURATED_ID,
DEFAULT_EMS_DARKMAP_ID,
EMS_APP_NAME,
} from './ems_defaults';
export const DEFAULT_EMS_FILE_API_URL = 'https://vector.maps.elastic.co';
export const DEFAULT_EMS_TILE_API_URL = 'https://tiles.maps.elastic.co';
export const DEFAULT_EMS_LANDING_PAGE_URL = 'https://maps.elastic.co/v8.0';
export const DEFAULT_EMS_FONT_LIBRARY_URL =
'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf';
export { EMSSettings } from './ems_settings';
export type { EMSConfig } from './ems_settings';
export const DEFAULT_EMS_ROADMAP_ID = 'road_map';
export const DEFAULT_EMS_ROADMAP_DESATURATED_ID = 'road_map_desaturated';
export const DEFAULT_EMS_DARKMAP_ID = 'dark_map';
export { ORIGIN } from './origin';
export const LICENSE_CHECK_ID = 'maps';

View file

@ -1,12 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export const ORIGIN = {
EMS: 'elastic_maps_service',
KIBANA_YML: 'self_hosted',
};

View file

@ -36,7 +36,7 @@ export const tilemapConfigSchema = schema.object({
options: tileMapConfigOptionsSchema,
});
export const emsConfigSchema = schema.object({
export const mapConfigSchema = schema.object({
tilemap: tilemapConfigSchema,
includeElasticMapsService: schema.boolean({ defaultValue: true }),
emsUrl: schema.string({ defaultValue: '' }),
@ -53,5 +53,5 @@ export const emsConfigSchema = schema.object({
}),
});
export type MapsEmsConfig = TypeOf<typeof emsConfigSchema>;
export type MapConfig = TypeOf<typeof mapConfigSchema>;
export type TileMapConfig = TypeOf<typeof tilemapConfigSchema>;

View file

@ -9,5 +9,8 @@
"configPath": ["map"],
"ui": true,
"server": true,
"optionalPlugins": [
"licensing"
],
"extraPublicDirs": ["common"]
}

View file

@ -6,30 +6,24 @@
* Side Public License, v 1.
*/
import { PluginInitializerContext } from 'kibana/public';
import type { PluginInitializerContext } from 'kibana/public';
import type { EMSClient } from '@elastic/ems-client';
import { MapsEmsPlugin } from './plugin';
import { IServiceSettings } from './service_settings';
import type { MapsEmsConfig } from '../config';
/** @public */
export type {
VectorLayer,
FileLayerField,
FileLayer,
TmsLayer,
IServiceSettings,
} from './service_settings';
import type { MapConfig } from '../config';
import type { EMSSettings } from '../common';
export function plugin(initializerContext: PluginInitializerContext) {
return new MapsEmsPlugin(initializerContext);
}
export { TMS_IN_YML_ID } from '../common';
export type { MapConfig, TileMapConfig } from '../config';
export type { EMSConfig } from '../common';
export type { MapsEmsConfig } from '../config';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface MapsEmsPluginPublicSetup {}
export interface MapsEmsPluginSetup {
config: MapsEmsConfig;
getServiceSettings: () => Promise<IServiceSettings>;
export interface MapsEmsPluginPublicStart {
config: MapConfig;
createEMSSettings(): EMSSettings;
createEMSClient(): Promise<EMSClient>;
}
export type MapsEmsPluginStart = ReturnType<MapsEmsPlugin['start']>;

View file

@ -6,12 +6,32 @@
* Side Public License, v 1.
*/
import type { MapsEmsConfig } from '../config';
import type { MapConfig } from '../config';
import { LicensingPluginStart } from '../../../../x-pack/plugins/licensing/public';
import { ILicense } from '../../../../x-pack/plugins/licensing/common/types';
import { LICENSE_CHECK_ID } from '../common';
let kibanaVersion: string;
export const setKibanaVersion = (version: string) => (kibanaVersion = version);
export const getKibanaVersion = (): string => kibanaVersion;
let mapsEmsConfig: MapsEmsConfig;
export const setMapsEmsConfig = (config: MapsEmsConfig) => (mapsEmsConfig = config);
export const getMapsEmsConfig = () => mapsEmsConfig;
let mapsEmsConfig: MapConfig;
export const setMapConfig = (mapsEms: MapConfig) => {
mapsEmsConfig = mapsEms;
};
export const getMapConfig = () => mapsEmsConfig;
let isEnterprisePlus: boolean = false;
function updateLicenseState(license: ILicense) {
const enterprise = license.check(LICENSE_CHECK_ID, 'enterprise');
isEnterprisePlus = enterprise.state === 'valid';
}
export function getIsEnterprisePlus() {
return isEnterprisePlus;
}
export async function setLicensingPluginStart(licensingPlugin: LicensingPluginStart) {
const license = await licensingPlugin.refresh();
updateLicenseState(license);
licensingPlugin.license$.subscribe(updateLicenseState);
}

View file

@ -0,0 +1,26 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import { EMSClient } from '@elastic/ems-client';
import { EMS_APP_NAME, EMSSettings } from '../../common';
export function createEMSClient(emsSettings: EMSSettings, kbnVersion: string): EMSClient {
return new EMSClient({
language: i18n.getLocale(),
appVersion: kbnVersion,
appName: EMS_APP_NAME,
tileApiUrl: emsSettings!.getEMSTileApiUrl(),
fileApiUrl: emsSettings!.getEMSFileApiUrl(),
landingPageUrl: emsSettings!.getEMSLandingPageUrl(),
fetchFunction(url: string) {
return fetch(url);
},
proxyPath: '',
});
}

View file

@ -1,29 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { IServiceSettings } from '../service_settings/service_settings_types';
import { getMapsEmsConfig } from '../kibana_services';
let loadPromise: Promise<IServiceSettings>;
export async function getServiceSettings(): Promise<IServiceSettings> {
if (typeof loadPromise !== 'undefined') {
return loadPromise;
}
loadPromise = new Promise(async (resolve, reject) => {
try {
const { ServiceSettings } = await import('./lazy');
const config = getMapsEmsConfig();
resolve(new ServiceSettings(config, config.tilemap));
} catch (error) {
reject(error);
}
});
return loadPromise;
}

View file

@ -0,0 +1,33 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { EMSClient } from '@elastic/ems-client';
import type { EMSSettings } from '../../common';
let lazyLoaded: (emsSettings: EMSSettings, version: string) => EMSClient;
export async function createEMSClientLazy(emsSettings: EMSSettings, version: string) {
if (lazyLoaded) {
return await lazyLoaded(emsSettings, version);
}
lazyLoaded = await new Promise(async (resolve, reject) => {
try {
try {
const { createEMSClient } = await import('./create_ems_client');
resolve(createEMSClient);
} catch (error) {
reject(error);
}
} catch (error) {
reject(error);
}
});
return await lazyLoaded(emsSettings, version);
}

View file

@ -1,9 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { ServiceSettings } from '../../service_settings/service_settings';

View file

@ -6,45 +6,53 @@
* Side Public License, v 1.
*/
// @ts-ignore
import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
// @ts-ignore
import { setKibanaVersion, setMapsEmsConfig } from './kibana_services';
// @ts-ignore
import { MapsEmsPluginSetup, MapsEmsPluginStart } from './index';
import type { MapsEmsConfig } from '../config';
import { getServiceSettings } from './lazy_load_bundle/get_service_settings';
import { CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
import {
setKibanaVersion,
setLicensingPluginStart,
setMapConfig,
getIsEnterprisePlus,
} from './kibana_services';
import type { MapsEmsPluginPublicSetup, MapsEmsPluginPublicStart } from './index';
import type { MapConfig } from '../config';
import { createEMSSettings } from '../common/ems_settings';
import type { LicensingPluginStart } from '../../../../x-pack/plugins/licensing/public';
import { createEMSClientLazy } from './lazy_load_bundle';
/**
* These are the interfaces with your public contracts. You should export these
* for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces.
* @public
*/
interface MapsEmsStartPublicDependencies {
licensing?: LicensingPluginStart;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface MapsEmsStartDependencies {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface MapsEmsSetupDependencies {}
export class MapsEmsPlugin implements Plugin<MapsEmsPluginPublicSetup, MapsEmsPluginPublicStart> {
readonly _initializerContext: PluginInitializerContext<MapConfig>;
export class MapsEmsPlugin implements Plugin<MapsEmsPluginSetup, MapsEmsPluginStart> {
readonly _initializerContext: PluginInitializerContext<MapsEmsConfig>;
constructor(initializerContext: PluginInitializerContext<MapsEmsConfig>) {
constructor(initializerContext: PluginInitializerContext<MapConfig>) {
this._initializerContext = initializerContext;
}
public setup(core: CoreSetup, plugins: MapsEmsSetupDependencies) {
const config = this._initializerContext.config.get<MapsEmsConfig>();
public setup() {
return {};
}
public start(code: CoreStart, plugins: MapsEmsStartPublicDependencies) {
const mapConfig = this._initializerContext.config.get<MapConfig>();
const kibanaVersion = this._initializerContext.env.packageInfo.version;
setKibanaVersion(kibanaVersion);
setMapsEmsConfig(config);
setMapConfig(mapConfig);
if (plugins.licensing) {
setLicensingPluginStart(plugins.licensing);
}
return {
getServiceSettings,
config,
config: mapConfig,
createEMSSettings: () => {
return createEMSSettings(mapConfig, getIsEnterprisePlus);
},
createEMSClient: async () => {
const emsSettings = createEMSSettings(mapConfig, getIsEnterprisePlus);
return createEMSClientLazy(emsSettings, kibanaVersion);
},
};
}
public start(core: CoreStart, plugins: MapsEmsStartDependencies) {}
}

View file

@ -1,9 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export * from './service_settings_types';

View file

@ -1,298 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
jest.mock('../kibana_services', () => ({
getKibanaVersion() {
return '1.2.3';
},
}));
import url from 'url';
import EMS_FILES from '../../__tests__/map/ems_mocks/sample_files.json';
import EMS_TILES from '../../__tests__/map/ems_mocks/sample_tiles.json';
import EMS_STYLE_ROAD_MAP_BRIGHT from '../../__tests__/map/ems_mocks/sample_style_bright';
import EMS_STYLE_ROAD_MAP_DESATURATED from '../../__tests__/map/ems_mocks/sample_style_desaturated';
import EMS_STYLE_DARK_MAP from '../../__tests__/map/ems_mocks/sample_style_dark';
import { ORIGIN } from '../../common';
import { ServiceSettings } from './service_settings';
describe('service_settings (FKA tile_map test)', function () {
const emsFileApiUrl = 'https://files.foobar';
const emsTileApiUrl = 'https://tiles.foobar';
const defaultMapConfig = {
emsFileApiUrl,
emsTileApiUrl,
includeElasticMapsService: true,
emsTileLayerId: {
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
},
};
const defaultTilemapConfig = {
options: {},
};
function makeServiceSettings(mapConfigOptions = {}, tilemapOptions = {}) {
const serviceSettings = new ServiceSettings(
{ ...defaultMapConfig, ...mapConfigOptions },
{ ...defaultTilemapConfig, ...tilemapOptions }
);
serviceSettings.__debugStubManifestCalls(async (url) => {
//simulate network calls
if (url.startsWith('https://tiles.foobar')) {
if (url.includes('/manifest')) {
return EMS_TILES;
} else if (url.includes('osm-bright-desaturated.json')) {
return EMS_STYLE_ROAD_MAP_DESATURATED;
} else if (url.includes('osm-bright.json')) {
return EMS_STYLE_ROAD_MAP_BRIGHT;
} else if (url.includes('dark-matter.json')) {
return EMS_STYLE_DARK_MAP;
}
} else if (url.startsWith('https://files.foobar')) {
return EMS_FILES;
}
});
return serviceSettings;
}
describe('TMS', function () {
it('should NOT get url from the config', async function () {
const serviceSettings = makeServiceSettings();
const tmsServices = await serviceSettings.getTMSServices();
const tmsService = tmsServices[0];
expect(typeof tmsService.url === 'undefined').toEqual(true);
});
it('should get url by resolving dynamically', async function () {
const serviceSettings = makeServiceSettings();
const tmsServices = await serviceSettings.getTMSServices();
const tmsService = tmsServices[0];
expect(typeof tmsService.url === 'undefined').toEqual(true);
const attrs = await serviceSettings.getAttributesForTMSLayer(tmsService);
expect(attrs.url.includes('{x}')).toEqual(true);
expect(attrs.url.includes('{y}')).toEqual(true);
expect(attrs.url.includes('{z}')).toEqual(true);
expect(attrs.attribution).toEqual(
'<a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">&lt;iframe id=\'iframe\' style=\'position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;\' src=\'http://256.256.256.256\'&gt;&lt;/iframe&gt;</a>'
);
const urlObject = url.parse(attrs.url, true);
expect(urlObject.hostname).toEqual('tiles.foobar');
expect(urlObject.query.my_app_name).toEqual('kibana');
expect(urlObject.query.elastic_tile_service_tos).toEqual('agree');
expect(typeof urlObject.query.my_app_version).toEqual('string');
});
it('should get options', async function () {
const serviceSettings = makeServiceSettings();
const tmsServices = await serviceSettings.getTMSServices();
const tmsService = tmsServices[0];
expect(typeof tmsService.minZoom).toEqual('number');
expect(typeof tmsService.maxZoom).toEqual('number');
expect(tmsService.attribution.includes('OpenStreetMap')).toEqual(true);
});
describe('tms mods', function () {
let serviceSettings;
it('should merge in tilemap url', async () => {
serviceSettings = makeServiceSettings(
{},
{
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
options: { minZoom: 0, maxZoom: 20 },
}
);
const tilemapServices = await serviceSettings.getTMSServices();
const expected = [
{
attribution: '',
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
id: 'TMS in config/kibana.yml',
},
{
id: 'road_map',
name: 'Road Map - Bright',
url: 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl',
minZoom: 0,
maxZoom: 10,
attribution:
'<a rel="noreferrer noopener" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a> | <a rel="noreferrer noopener" href="https://openmaptiles.org">OpenMapTiles</a> | <a rel="noreferrer noopener" href="https://www.maptiler.com">MapTiler</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">&lt;iframe id=\'iframe\' style=\'position:fixed;height: 40%;width: 100%;top: 60%;left: 5%;right:5%;border: 0px;background:white;\' src=\'http://256.256.256.256\'&gt;&lt;/iframe&gt;</a>',
subdomains: [],
},
];
const assertions = tilemapServices.map(async (actualService, index) => {
const expectedService = expected[index];
expect(actualService.id).toEqual(expectedService.id);
expect(actualService.attribution).toEqual(expectedService.attribution);
const attrs = await serviceSettings.getAttributesForTMSLayer(actualService);
expect(attrs.url).toEqual(expectedService.url);
});
return Promise.all(assertions);
});
it('should load appropriate EMS attributes for desaturated and dark theme', async () => {
serviceSettings = makeServiceSettings();
const tilemapServices = await serviceSettings.getTMSServices();
const roadMapService = tilemapServices.find((service) => service.id === 'road_map');
const desaturationFalse = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
false,
false
);
const desaturationTrue = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
true,
false
);
const darkThemeDesaturationFalse = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
false,
true
);
const darkThemeDesaturationTrue = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
true,
true
);
expect(desaturationFalse.url).toEqual(
'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(desaturationFalse.maxZoom).toEqual(10);
expect(desaturationTrue.url).toEqual(
'https://tiles.foobar/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(desaturationTrue.maxZoom).toEqual(18);
expect(darkThemeDesaturationFalse.url).toEqual(
'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(darkThemeDesaturationFalse.maxZoom).toEqual(22);
expect(darkThemeDesaturationTrue.url).toEqual(
'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(darkThemeDesaturationTrue.maxZoom).toEqual(22);
});
it('should exclude EMS', async () => {
serviceSettings = makeServiceSettings(
{
includeElasticMapsService: false,
},
{
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
options: { minZoom: 0, maxZoom: 20 },
}
);
const tilemapServices = await serviceSettings.getTMSServices();
const expected = [
{
attribution: '',
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
id: 'TMS in config/kibana.yml',
},
];
expect(tilemapServices.length).toEqual(1);
expect(tilemapServices[0].attribution).toEqual(expected[0].attribution);
expect(tilemapServices[0].id).toEqual(expected[0].id);
const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]);
expect(attrs.url).toEqual(expected[0].url);
});
it('should exclude all when not configured', async () => {
serviceSettings = makeServiceSettings({
includeElasticMapsService: false,
});
const tilemapServices = await serviceSettings.getTMSServices();
const expected = [];
expect(tilemapServices).toEqual(expected);
});
});
});
describe('File layers', function () {
it('should load manifest (all props)', async function () {
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
expect(fileLayers.length).toEqual(19);
const assertions = fileLayers.map(async function (fileLayer) {
expect(fileLayer.origin).toEqual(ORIGIN.EMS);
const fileUrl = await serviceSettings.getUrlForRegionLayer(fileLayer);
const urlObject = url.parse(fileUrl, true);
Object.keys({ elastic_tile_service_tos: 'agree' }).forEach((key) => {
expect(typeof urlObject.query[key]).toEqual('string');
});
});
return Promise.all(assertions);
});
it('should load manifest (individual props)', async () => {
const expected = {
attribution:
'<a rel="noreferrer noopener" href="http://www.naturalearthdata.com/about/terms-of-use">Made with NaturalEarth</a> | <a rel="noreferrer noopener" href="https://www.elastic.co/elastic-maps-service">Elastic Maps Service</a>',
format: 'geojson',
fields: [
{ type: 'id', name: 'iso2', description: 'ISO 3166-1 alpha-2 code' },
{ type: 'id', name: 'iso3', description: 'ISO 3166-1 alpha-3 code' },
{ type: 'property', name: 'name', description: 'name' },
],
created_at: '2017-04-26T17:12:15.978370', //not present in 6.6
name: 'World Countries',
};
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
const actual = fileLayers[0];
expect(expected.attribution).toEqual(actual.attribution);
expect(expected.format).toEqual(actual.format);
expect(expected.fields).toEqual(actual.fields);
expect(expected.name).toEqual(actual.name);
expect(expected.created_at).toEqual(actual.created_at);
});
it('should exclude all when not configured', async () => {
const serviceSettings = makeServiceSettings({
includeElasticMapsService: false,
});
const fileLayers = await serviceSettings.getFileLayers();
const expected = [];
expect(fileLayers).toEqual(expected);
});
it('should get hotlink', async () => {
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
const hotlink = await serviceSettings.getEMSHotLink(fileLayers[0]);
expect(hotlink).toEqual('?locale=en#file/world_countries'); //url host undefined becuase emsLandingPageUrl is set at kibana-load
});
it('should sanitize EMS attribution', async () => {
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
const fileLayer = fileLayers.find((layer) => {
return layer.id === 'world_countries_with_compromised_attribution';
});
expect(fileLayer.attribution).toEqual(
'<a rel="noreferrer noopener" href="http://www.naturalearthdata.com/about/terms-of-use">&lt;div onclick=\'alert(1\')&gt;Made with NaturalEarth&lt;/div&gt;</a> | <a rel="noreferrer noopener">Elastic Maps Service</a>'
);
});
});
});

View file

@ -12,9 +12,13 @@ import {
Plugin,
PluginConfigDescriptor,
} from 'src/core/server';
import { MapsEmsConfig, emsConfigSchema } from '../config';
import { MapConfig, mapConfigSchema } from '../config';
import { EMSSettings, LICENSE_CHECK_ID } from '../common';
import { LicensingPluginSetup } from '../../../../x-pack/plugins/licensing/server';
import { ILicense } from '../../../../x-pack/plugins/licensing/common/types';
export type { EMSSettings } from '../common';
export const config: PluginConfigDescriptor<MapsEmsConfig> = {
export const config: PluginConfigDescriptor<MapConfig> = {
exposeToBrowser: {
tilemap: true,
includeElasticMapsService: true,
@ -25,24 +29,46 @@ export const config: PluginConfigDescriptor<MapsEmsConfig> = {
emsFontLibraryUrl: true,
emsTileLayerId: true,
},
schema: emsConfigSchema,
schema: mapConfigSchema,
};
export interface MapsEmsPluginSetup {
config: MapsEmsConfig;
export interface MapsEmsPluginServerSetup {
config: MapConfig;
createEMSSettings: () => EMSSettings;
}
export class MapsEmsPlugin implements Plugin<MapsEmsPluginSetup> {
readonly _initializerContext: PluginInitializerContext<MapsEmsConfig>;
interface MapsEmsSetupServerDependencies {
licensing?: LicensingPluginSetup;
}
constructor(initializerContext: PluginInitializerContext<MapsEmsConfig>) {
export class MapsEmsPlugin implements Plugin<MapsEmsPluginServerSetup> {
readonly _initializerContext: PluginInitializerContext<MapConfig>;
constructor(initializerContext: PluginInitializerContext<MapConfig>) {
this._initializerContext = initializerContext;
}
public setup(core: CoreSetup) {
const emsPluginConfig = this._initializerContext.config.get();
public setup(core: CoreSetup, plugins: MapsEmsSetupServerDependencies) {
const mapConfig = this._initializerContext.config.get();
let isEnterprisePlus = false;
if (plugins.licensing) {
function updateLicenseState(license: ILicense) {
const enterprise = license.check(LICENSE_CHECK_ID, 'enterprise');
isEnterprisePlus = enterprise.state === 'valid';
}
plugins.licensing.refresh().then(updateLicenseState);
plugins.licensing.license$.subscribe(updateLicenseState);
}
return {
config: emsPluginConfig,
config: mapConfig,
createEMSSettings: () => {
return new EMSSettings(mapConfig, () => {
return isEnterprisePlus;
});
},
};
}

View file

@ -7,5 +7,8 @@
"declarationMap": true
},
"include": ["common/**/*", "public/**/*", "server/**/*", "./config.ts"],
"references": [{ "path": "../../core/tsconfig.json" }]
"references": [
{ "path": "../../core/tsconfig.json" },
{ "path": "../../../x-pack/plugins/licensing/tsconfig.json" }
]
}

View file

@ -9,7 +9,10 @@
import { i18n } from '@kbn/i18n';
// @ts-ignore
import { bypassExternalUrlCheck } from '../vega_view/vega_base_view';
import { IServiceSettings, FileLayer } from '../../../../maps_ems/public';
import type {
IServiceSettings,
FileLayer,
} from '../vega_view/vega_map_view/service_settings/service_settings_types';
import { Data, UrlObject, EmsQueryRequest } from './types';
/**

View file

@ -22,7 +22,7 @@ import { EmsFileParser } from './ems_file_parser';
import { UrlParser } from './url_parser';
import { SearchAPI } from './search_api';
import { TimeCache } from './time_cache';
import { IServiceSettings } from '../../../../maps_ems/public';
import type { IServiceSettings } from '../vega_view/vega_map_view/service_settings/service_settings_types';
import {
Bool,
Data,

View file

@ -18,18 +18,20 @@ import {
setInjectedVars,
setUISettings,
setInjectedMetadata,
setMapServiceSettings,
setDocLinks,
setMapsEms,
} from './services';
import { createVegaFn } from './vega_fn';
import { createVegaTypeDefinition } from './vega_type';
import { IServiceSettings, MapsEmsPluginSetup } from '../../../maps_ems/public';
import type { MapsEmsPluginPublicStart } from '../../../maps_ems/public';
import type { IServiceSettings } from './vega_view/vega_map_view/service_settings/service_settings_types';
import { ConfigSchema } from '../config';
import { getVegaInspectorView } from './vega_inspector';
import { getVegaVisRenderer } from './vega_vis_renderer';
import { MapServiceSettings } from './vega_view/vega_map_view/map_service_settings';
import { getServiceSettingsLazy } from './vega_view/vega_map_view/service_settings/get_service_settings_lazy';
/** @internal */
export interface VegaVisualizationDependencies {
@ -46,12 +48,12 @@ export interface VegaPluginSetupDependencies {
visualizations: VisualizationsSetup;
inspector: InspectorSetup;
data: DataPublicPluginSetup;
mapsEms: MapsEmsPluginSetup;
}
/** @internal */
export interface VegaPluginStartDependencies {
data: DataPublicPluginStart;
mapsEms: MapsEmsPluginPublicStart;
}
/** @internal */
@ -64,7 +66,7 @@ export class VegaPlugin implements Plugin<void, void> {
public setup(
core: CoreSetup,
{ inspector, data, expressions, visualizations, mapsEms }: VegaPluginSetupDependencies
{ inspector, data, expressions, visualizations }: VegaPluginSetupDependencies
) {
setInjectedVars({
enableExternalUrls: this.initializerContext.config.get().enableExternalUrls,
@ -73,16 +75,12 @@ export class VegaPlugin implements Plugin<void, void> {
setUISettings(core.uiSettings);
setMapServiceSettings(
new MapServiceSettings(mapsEms.config, this.initializerContext.env.packageInfo.version)
);
const visualizationDependencies: Readonly<VegaVisualizationDependencies> = {
core,
plugins: {
data,
},
getServiceSettings: mapsEms.getServiceSettings,
getServiceSettings: getServiceSettingsLazy,
};
inspector.registerView(getVegaInspectorView({ uiSettings: core.uiSettings }));
@ -93,10 +91,11 @@ export class VegaPlugin implements Plugin<void, void> {
visualizations.createBaseVisualization(createVegaTypeDefinition());
}
public start(core: CoreStart, { data }: VegaPluginStartDependencies) {
public start(core: CoreStart, { data, mapsEms }: VegaPluginStartDependencies) {
setNotifications(core.notifications);
setData(data);
setInjectedMetadata(core.injectedMetadata);
setDocLinks(core.docLinks);
setMapsEms(mapsEms);
}
}

View file

@ -10,7 +10,7 @@ import { CoreStart, NotificationsStart, IUiSettingsClient, DocLinksStart } from
import { DataPublicPluginStart } from '../../../data/public';
import { createGetterSetter } from '../../../kibana_utils/public';
import { MapServiceSettings } from './vega_view/vega_map_view/map_service_settings';
import type { MapsEmsPluginPublicStart } from '../../../maps_ems/public';
export const [getData, setData] = createGetterSetter<DataPublicPluginStart>('Data');
@ -18,13 +18,11 @@ export const [getNotifications, setNotifications] =
createGetterSetter<NotificationsStart>('Notifications');
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
export const [getMapsEms, setMapsEms] = createGetterSetter<MapsEmsPluginPublicStart>('mapsEms');
export const [getInjectedMetadata, setInjectedMetadata] =
createGetterSetter<CoreStart['injectedMetadata']>('InjectedMetadata');
export const [getMapServiceSettings, setMapServiceSettings] =
createGetterSetter<MapServiceSettings>('MapServiceSettings');
export const [getInjectedVars, setInjectedVars] = createGetterSetter<{
enableExternalUrls: boolean;
emsTileLayerId: unknown;

View file

@ -9,7 +9,7 @@
import type { IExternalUrl } from 'kibana/public';
import { DataPublicPluginStart } from 'src/plugins/data/public';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { IServiceSettings } from 'src/plugins/maps_ems/public';
import type { IServiceSettings } from './vega_map_view/service_settings/service_settings_types';
import { VegaParser } from '../data_model/vega_parser';
import { createVegaStateRestorer } from '../lib/vega_state_restorer';

View file

@ -7,10 +7,8 @@
*/
import type { Style } from '@kbn/mapbox-gl';
import { TMS_IN_YML_ID } from '../../../../../maps_ems/public';
export const vegaLayerId = 'vega';
export const userConfiguredLayerId = TMS_IN_YML_ID;
export const defaultMapConfig = {
maxZoom: 20,
minZoom: 0,

View file

@ -1,106 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { get } from 'lodash';
import { uiSettingsServiceMock } from 'src/core/public/mocks';
import { MapServiceSettings, getAttributionsForTmsService } from './map_service_settings';
import type { MapsEmsConfig } from '../../../../../maps_ems/public';
import { EMSClient, TMSService } from '@elastic/ems-client';
import { setUISettings } from '../../services';
const getPrivateField = <T>(mapServiceSettings: MapServiceSettings, privateField: string) =>
get(mapServiceSettings, privateField) as T;
describe('vega_map_view/map_service_settings', () => {
describe('MapServiceSettings', () => {
const appVersion = '99';
let config: MapsEmsConfig;
let getUiSettingsMockedValue: any;
beforeEach(() => {
config = {
emsTileLayerId: {
desaturated: 'road_map_desaturated',
dark: 'dark_map',
},
} as MapsEmsConfig;
setUISettings({
...uiSettingsServiceMock.createSetupContract(),
get: () => getUiSettingsMockedValue,
});
});
test('should be able to create instance of MapServiceSettings', () => {
const mapServiceSettings = new MapServiceSettings(config, appVersion);
expect(mapServiceSettings instanceof MapServiceSettings).toBeTruthy();
expect(mapServiceSettings.hasUserConfiguredTmsLayer()).toBeFalsy();
expect(mapServiceSettings.defaultTmsLayer()).toBe('road_map_desaturated');
});
test('should be able to set user configured base layer through config', () => {
const mapServiceSettings = new MapServiceSettings(
{
...config,
tilemap: {
url: 'http://some.tile.com/map/{z}/{x}/{y}.jpg',
options: {
attribution: 'attribution',
minZoom: 0,
maxZoom: 4,
},
},
},
appVersion
);
expect(mapServiceSettings.defaultTmsLayer()).toBe('TMS in config/kibana.yml');
expect(mapServiceSettings.hasUserConfiguredTmsLayer()).toBeTruthy();
});
test('should load ems client only on executing getTmsService method', async () => {
const mapServiceSettings = new MapServiceSettings(config, appVersion);
expect(getPrivateField<EMSClient>(mapServiceSettings, 'emsClient')).toBeUndefined();
await mapServiceSettings.getTmsService('road_map');
expect(
getPrivateField<EMSClient>(mapServiceSettings, 'emsClient') instanceof EMSClient
).toBeTruthy();
});
test('should set isDarkMode value on executing getTmsService method', async () => {
const mapServiceSettings = new MapServiceSettings(config, appVersion);
getUiSettingsMockedValue = true;
expect(getPrivateField<EMSClient>(mapServiceSettings, 'isDarkMode')).toBeFalsy();
await mapServiceSettings.getTmsService('road_map');
expect(getPrivateField<EMSClient>(mapServiceSettings, 'isDarkMode')).toBeTruthy();
});
test('getAttributionsForTmsService method should return attributes in a correct form', () => {
const tmsService = {
getAttributions: jest.fn(() => [
{ url: 'https://fist_attr.com', label: 'fist_attr' },
{ url: 'https://second_attr.com', label: 'second_attr' },
]),
} as unknown as TMSService;
expect(getAttributionsForTmsService(tmsService)).toMatchInlineSnapshot(`
Array [
"<a rel=\\"noreferrer noopener\\" href=\\"https://fist_attr.com\\">fist_attr</a>",
"<a rel=\\"noreferrer noopener\\" href=\\"https://second_attr.com\\">second_attr</a>",
]
`);
});
});
});

View file

@ -1,94 +0,0 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import type { EMSClient, TMSService } from '@elastic/ems-client';
import { getUISettings } from '../../services';
import { userConfiguredLayerId } from './constants';
import type { MapsEmsConfig } from '../../../../../maps_ems/public';
type EmsClientConfig = ConstructorParameters<typeof EMSClient>[0];
const hasUserConfiguredTmsService = (config: MapsEmsConfig) => Boolean(config.tilemap?.url);
const initEmsClientAsync = async (config: Partial<EmsClientConfig>) => {
/**
* Build optimization: '@elastic/ems-client' should be loaded from a separate chunk
*/
const emsClientModule = await import('@elastic/ems-client');
return new emsClientModule.EMSClient({
language: i18n.getLocale(),
appName: 'kibana',
// Wrap to avoid errors passing window fetch
fetchFunction(input: RequestInfo, init?: RequestInit) {
return fetch(input, init);
},
...config,
} as EmsClientConfig);
};
export class MapServiceSettings {
private emsClient?: EMSClient;
private isDarkMode: boolean = false;
constructor(public config: MapsEmsConfig, private appVersion: string) {}
private isInitialized() {
return Boolean(this.emsClient);
}
public hasUserConfiguredTmsLayer() {
return hasUserConfiguredTmsService(this.config);
}
public defaultTmsLayer() {
const { dark, desaturated } = this.config.emsTileLayerId;
if (this.hasUserConfiguredTmsLayer()) {
return userConfiguredLayerId;
}
return this.isDarkMode ? dark : desaturated;
}
private async initialize() {
this.isDarkMode = getUISettings().get('theme:darkMode');
this.emsClient = await initEmsClientAsync({
appVersion: this.appVersion,
fileApiUrl: this.config.emsFileApiUrl,
tileApiUrl: this.config.emsTileApiUrl,
landingPageUrl: this.config.emsLandingPageUrl,
});
// Allow zooms > 10 for Vega Maps
// any kibana user, regardless of distribution, should get all zoom levels
// use `sspl` license to indicate this
this.emsClient.addQueryParams({ license: 'sspl' });
}
public async getTmsService(tmsTileLayer: string) {
if (!this.isInitialized()) {
await this.initialize();
}
return this.emsClient?.findTMSServiceById(tmsTileLayer);
}
}
export function getAttributionsForTmsService(tmsService: TMSService) {
return tmsService.getAttributions().map(({ label, url }) => {
const anchorTag = document.createElement('a');
anchorTag.textContent = label;
anchorTag.setAttribute('rel', 'noreferrer noopener');
anchorTag.setAttribute('href', url);
return anchorTag.outerHTML;
});
}

View file

@ -0,0 +1,22 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { IServiceSettings } from './service_settings_types';
import { ServiceSettings } from './service_settings';
import { getMapsEms } from '../../../services';
import type { MapsEmsPluginPublicStart, MapConfig } from '../../../../../../maps_ems/public';
export async function getServiceSettings(): Promise<IServiceSettings> {
const mapsEms: MapsEmsPluginPublicStart = getMapsEms();
const mapsEmsConfig: MapConfig = mapsEms.config;
const emsClient = await mapsEms.createEMSClient();
// any kibana user, regardless of distribution, should get all zoom levels
// use `sspl` license to indicate this
emsClient.addQueryParams({ license: 'sspl' });
return new ServiceSettings(mapsEmsConfig, mapsEmsConfig.tilemap, emsClient);
}

View file

@ -0,0 +1,32 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { IServiceSettings } from './service_settings_types';
let lazyLoaded: () => Promise<IServiceSettings>;
export async function getServiceSettingsLazy(): Promise<IServiceSettings> {
if (lazyLoaded) {
return await lazyLoaded();
}
lazyLoaded = await new Promise(async (resolve, reject) => {
try {
try {
const { getServiceSettings } = await import('./get_service_settings');
resolve(getServiceSettings);
} catch (error) {
reject(error);
}
} catch (error) {
reject(error);
}
});
return await lazyLoaded();
}

View file

@ -0,0 +1,261 @@
/*
* 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ServiceSettings } from './service_settings';
function makeMockLayer({ id, min, max, attributions, url }) {
return {
getId() {
return id;
},
getMinZoom() {
return min;
},
getMaxZoom() {
return max;
},
getAttributions() {
return attributions;
},
getUrlTemplate() {
return url;
},
};
}
function createMockEMSClient() {
return {
addQueryParams() {},
getFileLayers() {
return [
{
getDefaultFormatType() {
return 'geojson';
},
getDefaultFormatMeta() {
return {};
},
getDisplayName() {
return 'Foobar Countries';
},
getId() {
return 'foobar_countries';
},
getCreatedAt() {
return {};
},
getFieldsInLanguage() {
return [];
},
getAttributions() {
return [{ url: 'http://foobar/com', label: 'foobar' }];
},
},
];
},
getTMSServices() {
return [
makeMockLayer({
id: 'road_map',
min: 0,
max: 10,
attributions: [{ url: 'https://foobar.com', label: 'foobar' }],
url: 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl',
}),
makeMockLayer({
id: 'road_map_desaturated',
min: 0,
max: 18,
attributions: [{ url: 'https://foobar.com', label: 'foobar' }],
url: 'https://tiles.foobar/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl',
}),
makeMockLayer({
id: 'dark_map',
min: 0,
max: 22,
attributions: [{ url: 'https://foobar.com', label: 'foobar' }],
url: 'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl',
}),
];
},
};
}
describe('service_settings (FKA tile_map test)', function () {
const emsFileApiUrl = 'https://files.foobar';
const emsTileApiUrl = 'https://tiles.foobar';
const defaultMapConfig = {
emsFileApiUrl,
emsTileApiUrl,
includeElasticMapsService: true,
emsTileLayerId: {
bright: 'road_map',
desaturated: 'road_map_desaturated',
dark: 'dark_map',
},
};
const defaultTilemapConfig = {
options: {},
};
function makeServiceSettings(mapConfigOptions = {}, tilemapOptions = {}) {
return new ServiceSettings(
{ ...defaultMapConfig, ...mapConfigOptions },
{ ...defaultTilemapConfig, ...tilemapOptions },
createMockEMSClient()
);
}
describe('tms mods', function () {
let serviceSettings;
it('should merge in tilemap url', async () => {
serviceSettings = makeServiceSettings(
{},
{
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
options: { minZoom: 0, maxZoom: 20 },
}
);
const tilemapServices = await serviceSettings.getTMSServices();
const expectedSelection = [
{
attribution: '',
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
id: 'TMS in config/kibana.yml',
},
{
id: 'road_map',
name: 'Road Map - Bright',
url: 'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl',
minZoom: 0,
maxZoom: 10,
attribution: `<a rel=\"noreferrer noopener\" href=\"https://foobar.com\">foobar</a>`,
subdomains: [],
},
];
expect(tilemapServices.length).toEqual(2); //needs to only include 1 base-map iso 3
const assertions = tilemapServices.map(async (actualService, index) => {
const expectedService = expectedSelection[index];
expect(actualService.id).toEqual(expectedService.id);
expect(actualService.attribution).toEqual(expectedService.attribution);
const attrs = await serviceSettings.getAttributesForTMSLayer(actualService);
expect(attrs.url).toEqual(expectedService.url);
});
return Promise.all(assertions);
});
it('should load appropriate EMS attributes for desaturated and dark theme', async () => {
serviceSettings = makeServiceSettings();
const tilemapServices = await serviceSettings.getTMSServices();
const roadMapService = tilemapServices.find((service) => service.id === 'road_map');
const desaturationFalse = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
false,
false
);
expect(desaturationFalse.url).toEqual(
'https://tiles.foobar/raster/styles/osm-bright/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(desaturationFalse.maxZoom).toEqual(10);
const desaturationTrue = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
true,
false
);
expect(desaturationTrue.url).toEqual(
'https://tiles.foobar/raster/styles/osm-bright-desaturated/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(desaturationTrue.maxZoom).toEqual(18);
const darkThemeDesaturationFalse = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
false,
true
);
expect(darkThemeDesaturationFalse.url).toEqual(
'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(darkThemeDesaturationFalse.maxZoom).toEqual(22);
const darkThemeDesaturationTrue = await serviceSettings.getAttributesForTMSLayer(
roadMapService,
true,
true
);
expect(darkThemeDesaturationTrue.url).toEqual(
'https://tiles.foobar/raster/styles/dark-matter/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana&my_app_version=1.2.3&license=sspl'
);
expect(darkThemeDesaturationTrue.maxZoom).toEqual(22);
});
it('should exclude EMS', async () => {
serviceSettings = makeServiceSettings(
{
includeElasticMapsService: false,
},
{
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
options: { minZoom: 0, maxZoom: 20 },
}
);
const tilemapServices = await serviceSettings.getTMSServices();
const expected = [
{
attribution: '',
url: 'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
id: 'TMS in config/kibana.yml',
},
];
expect(tilemapServices.length).toEqual(1);
expect(tilemapServices[0].attribution).toEqual(expected[0].attribution);
expect(tilemapServices[0].id).toEqual(expected[0].id);
const attrs = await serviceSettings.getAttributesForTMSLayer(tilemapServices[0]);
expect(attrs.url).toEqual(expected[0].url);
});
it('should exclude all when no tilemap configured in the yml', async () => {
serviceSettings = makeServiceSettings({
includeElasticMapsService: false,
});
const tilemapServices = await serviceSettings.getTMSServices();
expect(tilemapServices).toEqual([]);
});
});
describe('File layers', function () {
it('should exclude all when not configured', async () => {
const serviceSettings = makeServiceSettings({
includeElasticMapsService: false,
});
const fileLayers = await serviceSettings.getFileLayers();
const expected = [];
expect(fileLayers).toEqual(expected);
});
it('should sanitize EMS attribution', async () => {
const serviceSettings = makeServiceSettings();
const fileLayers = await serviceSettings.getFileLayers();
const fileLayer = fileLayers.find((layer) => {
return layer.id === 'foobar_countries';
});
expect(fileLayer.attribution).toEqual(
`<a rel=\"noreferrer noopener\" href=\"http://foobar/com\">foobar</a>`
);
});
});
});

View file

@ -8,44 +8,26 @@
import _ from 'lodash';
import MarkdownIt from 'markdown-it';
import { EMSClient, FileLayer as EMSFileLayer, TMSService } from '@elastic/ems-client';
import { i18n } from '@kbn/i18n';
import { getKibanaVersion } from '../kibana_services';
import { FileLayer, IServiceSettings, TmsLayer } from './service_settings_types';
import { ORIGIN, TMS_IN_YML_ID } from '../../common';
import type { MapsEmsConfig, TileMapConfig } from '../../config';
import type { EMSClient, FileLayer as EMSFileLayer, TMSService } from '@elastic/ems-client';
import type { FileLayer, IServiceSettings, TmsLayer } from './service_settings_types';
import { ORIGIN_LEGACY, TMS_IN_YML_ID } from './service_settings_types';
import type { MapConfig, TileMapConfig } from '../../../../../../maps_ems/public';
/**
* This class provides access to the EMS-layers and the kibana.yml configured layers through a single interface.
*/
export class ServiceSettings implements IServiceSettings {
private readonly _mapConfig: MapsEmsConfig;
private readonly _mapConfig: MapConfig;
private readonly _tilemapsConfig: TileMapConfig;
private readonly _hasTmsConfigured: boolean;
private readonly _emsClient: EMSClient;
private readonly tmsOptionsFromConfig: any;
constructor(mapConfig: MapsEmsConfig, tilemapsConfig: TileMapConfig) {
constructor(mapConfig: MapConfig, tilemapsConfig: TileMapConfig, emsClient: EMSClient) {
this._mapConfig = mapConfig;
this._tilemapsConfig = tilemapsConfig;
this._hasTmsConfigured = typeof tilemapsConfig.url === 'string' && tilemapsConfig.url !== '';
this._emsClient = new EMSClient({
language: i18n.getLocale(),
appVersion: getKibanaVersion(),
appName: 'kibana',
fileApiUrl: this._mapConfig.emsFileApiUrl,
tileApiUrl: this._mapConfig.emsTileApiUrl,
landingPageUrl: this._mapConfig.emsLandingPageUrl,
// Wrap to avoid errors passing window fetch
fetchFunction(...args: any[]) {
// @ts-expect-error
return fetch(...args);
},
});
// any kibana user, regardless of distribution, should get all zoom levels
// use `sspl` license to indicate this
this._emsClient.addQueryParams({ license: 'sspl' });
this._emsClient = emsClient;
const markdownIt = new MarkdownIt({
html: false,
@ -59,22 +41,12 @@ export class ServiceSettings implements IServiceSettings {
});
}
__debugStubManifestCalls(manifestRetrieval: () => Promise<unknown>): { removeStub: () => void } {
const oldGetManifest = this._emsClient.getManifest;
getMapConfig(): MapConfig {
return this._mapConfig;
}
// This legacy code used for debugging/testing only.
// @ts-expect-error
this._emsClient.getManifest = manifestRetrieval;
return {
removeStub: () => {
// @ts-expect-error
delete this._emsClient.getManifest;
// not strictly necessary since this is prototype method
if (this._emsClient.getManifest !== oldGetManifest) {
this._emsClient.getManifest = oldGetManifest;
}
},
};
getTileMapConfig(): TileMapConfig {
return this._tilemapsConfig;
}
_backfillSettings = (fileLayer: EMSFileLayer): FileLayer => {
@ -85,7 +57,7 @@ export class ServiceSettings implements IServiceSettings {
return {
name: fileLayer.getDisplayName(),
origin: fileLayer.getOrigin(),
origin: ORIGIN_LEGACY.EMS,
id: fileLayer.getId(),
created_at: fileLayer.getCreatedAt(),
attribution: getAttributionString(fileLayer),
@ -115,7 +87,7 @@ export class ServiceSettings implements IServiceSettings {
const tmsService: TmsLayer = {
..._.cloneDeep(this.tmsOptionsFromConfig),
id: TMS_IN_YML_ID,
origin: ORIGIN.KIBANA_YML,
origin: ORIGIN_LEGACY.KIBANA_YML,
};
allServices.push(tmsService);
@ -129,7 +101,7 @@ export class ServiceSettings implements IServiceSettings {
.map(async (tmsService: TMSService) => {
// shim for compatibility
return {
origin: tmsService.getOrigin(),
origin: ORIGIN_LEGACY.EMS,
id: tmsService.getId(),
minZoom: (await tmsService.getMinZoom()) as number,
maxZoom: (await tmsService.getMaxZoom()) as number,
@ -143,33 +115,18 @@ export class ServiceSettings implements IServiceSettings {
return allServices;
}
/**
* Set optional query-parameters for all requests
*
* @param additionalQueryParams
*/
setQueryParams(additionalQueryParams: { [p: string]: string }) {
// Functions more as a "set" than an "add" in ems-client
this._emsClient.addQueryParams(additionalQueryParams);
async getTmsService(id: string) {
return this._emsClient.findTMSServiceById(id);
}
async getFileLayerFromConfig(fileLayerConfig: FileLayer): Promise<EMSFileLayer | undefined> {
const fileLayers = await this._emsClient.getFileLayers();
return fileLayers.find((fileLayer) => {
const hasIdByName = fileLayer.hasId(fileLayerConfig.name); // legacy
const hasIdById = fileLayer.hasId(fileLayerConfig.id);
return hasIdByName || hasIdById;
});
}
async getDefaultTmsLayer(isDarkMode: boolean): Promise<string> {
const { dark, desaturated } = this._mapConfig.emsTileLayerId;
async getEMSHotLink(fileLayerConfig: FileLayer): Promise<string | null> {
const layer = await this.getFileLayerFromConfig(fileLayerConfig);
return layer ? layer.getEMSHotLink() : null;
}
if (hasUserConfiguredTmsLayer(this._mapConfig)) {
return TMS_IN_YML_ID;
}
async loadFileLayerConfig(fileLayerConfig: FileLayer): Promise<FileLayer | null> {
const fileLayer = await this.getFileLayerFromConfig(fileLayerConfig);
return fileLayer ? this._backfillSettings(fileLayer) : null;
return isDarkMode ? dark : desaturated;
}
async _getAttributesForEMSTMSLayer(isDesaturated: boolean, isDarkMode: boolean) {
@ -193,7 +150,7 @@ export class ServiceSettings implements IServiceSettings {
minZoom: await tmsService!.getMinZoom(),
maxZoom: await tmsService!.getMaxZoom(),
attribution: getAttributionString(tmsService!),
origin: ORIGIN.EMS,
origin: ORIGIN_LEGACY.EMS,
};
}
@ -202,16 +159,16 @@ export class ServiceSettings implements IServiceSettings {
isDesaturated: boolean,
isDarkMode: boolean
) {
if (tmsServiceConfig.origin === ORIGIN.EMS) {
if (tmsServiceConfig.origin === ORIGIN_LEGACY.EMS) {
return this._getAttributesForEMSTMSLayer(isDesaturated, isDarkMode);
} else if (tmsServiceConfig.origin === ORIGIN.KIBANA_YML) {
} else if (tmsServiceConfig.origin === ORIGIN_LEGACY.KIBANA_YML) {
const attrs = _.pick(this._tilemapsConfig, ['url', 'minzoom', 'maxzoom', 'attribution']);
return { ...attrs, ...{ origin: ORIGIN.KIBANA_YML } };
return { ...attrs, ...{ origin: ORIGIN_LEGACY.KIBANA_YML } };
} else {
// this is an older config. need to resolve this dynamically.
if (tmsServiceConfig.id === TMS_IN_YML_ID) {
const attrs = _.pick(this._tilemapsConfig, ['url', 'minzoom', 'maxzoom', 'attribution']);
return { ...attrs, ...{ origin: ORIGIN.KIBANA_YML } };
return { ...attrs, ...{ origin: ORIGIN_LEGACY.KIBANA_YML } };
} else {
// assume ems
return this._getAttributesForEMSTMSLayer(isDesaturated, isDarkMode);
@ -236,14 +193,17 @@ export class ServiceSettings implements IServiceSettings {
async getUrlForRegionLayer(fileLayerConfig: FileLayer): Promise<string | undefined> {
let url;
if (fileLayerConfig.origin === ORIGIN.EMS) {
if (fileLayerConfig.origin === ORIGIN_LEGACY.EMS) {
url = this._getFileUrlFromEMS(fileLayerConfig);
} else if (fileLayerConfig.layerId && fileLayerConfig.layerId.startsWith(`${ORIGIN.EMS}.`)) {
} else if (
fileLayerConfig.layerId &&
fileLayerConfig.layerId.startsWith(`${ORIGIN_LEGACY.EMS}.`)
) {
// fallback for older saved objects
url = this._getFileUrlFromEMS(fileLayerConfig);
} else if (
fileLayerConfig.layerId &&
fileLayerConfig.layerId.startsWith(`${ORIGIN.KIBANA_YML}.`)
fileLayerConfig.layerId.startsWith(`${ORIGIN_LEGACY.KIBANA_YML}.`)
) {
// fallback for older saved objects
url = fileLayerConfig.url;
@ -254,14 +214,12 @@ export class ServiceSettings implements IServiceSettings {
return url;
}
async getJsonForRegionLayer(fileLayerConfig: FileLayer) {
const url = await this.getUrlForRegionLayer(fileLayerConfig);
const response = await fetch(url!);
return await response.json();
getAttributionsFromTMSServce(tmsService: TMSService): string {
return getAttributionString(tmsService);
}
}
function getAttributionString(emsService: EMSFileLayer | TMSService) {
function getAttributionString(emsService: EMSFileLayer | TMSService): string {
const attributions = emsService.getAttributions();
const attributionSnippets = attributions.map((attribution) => {
const anchorTag = document.createElement('a');
@ -274,3 +232,7 @@ function getAttributionString(emsService: EMSFileLayer | TMSService) {
});
return attributionSnippets.join(' | '); // !!!this is the current convention used in Kibana
}
function hasUserConfiguredTmsLayer(config: MapConfig) {
return Boolean(config.tilemap?.url);
}

View file

@ -5,6 +5,15 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { TMSService } from '@elastic/ems-client';
import type { MapConfig, TileMapConfig } from '../../../../../../maps_ems/public';
export const ORIGIN_LEGACY = {
EMS: 'elastic_maps_service',
KIBANA_YML: 'self_hosted',
};
export const TMS_IN_YML_ID = 'TMS in config/kibana.yml';
export interface TmsLayer {
id: string;
@ -41,14 +50,18 @@ export interface VectorLayer extends FileLayer {
}
export interface IServiceSettings {
getEMSHotLink(layer: FileLayer): Promise<string | null>;
getTMSServices(): Promise<TmsLayer[]>;
getFileLayers(): Promise<FileLayer[]>;
getUrlForRegionLayer(layer: FileLayer): Promise<string | undefined>;
setQueryParams(params: { [p: string]: string }): void;
getAttributesForTMSLayer(
tmsServiceConfig: TmsLayer,
isDesaturated: boolean,
isDarkMode: boolean
): any;
getDefaultTmsLayer(isDarkMode: boolean): Promise<string>;
getTmsService(id: string): Promise<TMSService | undefined>;
getMapConfig(): MapConfig;
getTileMapConfig(): TileMapConfig;
getAttributionsFromTMSServce(tmsService: TMSService): string;
}

View file

@ -8,7 +8,6 @@
import 'jest-canvas-mock';
import type { TMSService } from '@elastic/ems-client';
import { VegaMapView } from './view';
import { VegaViewParams } from '../vega_base_view';
import { VegaParser } from '../../data_model/vega_parser';
@ -17,16 +16,9 @@ import { SearchAPI } from '../../data_model/search_api';
import vegaMap from '../../test_utils/vega_map_test.json';
import { coreMock } from '../../../../../../core/public/mocks';
import { dataPluginMock } from '../../../../../data/public/mocks';
import type { IServiceSettings, MapsEmsConfig } from '../../../../../maps_ems/public';
import { MapServiceSettings } from './map_service_settings';
import { userConfiguredLayerId } from './constants';
import {
setInjectedVars,
setData,
setNotifications,
setMapServiceSettings,
setUISettings,
} from '../../services';
import type { IServiceSettings } from '../vega_map_view/service_settings/service_settings_types';
import { setInjectedVars, setData, setNotifications, setUISettings } from '../../services';
import { initVegaLayer, initTmsRasterLayer } from './layers';
import { mapboxgl } from '@kbn/mapbox-gl';
@ -63,10 +55,41 @@ jest.mock('./layers', () => ({
describe('vega_map_view/view', () => {
describe('VegaMapView', () => {
let isUserProvided = true;
const coreStart = coreMock.createStart();
const dataPluginStart = dataPluginMock.createStartContract();
const mockGetServiceSettings = async () => {
return {} as IServiceSettings;
return {
getAttributionsFromTMSServce() {
return [`<a rel=\"noreferrer noopener\" href=\"tms_attributions\"></a>`];
},
getTmsService() {
return {
getVectorStyleSheet: () => ({
version: 8,
sources: {},
// @ts-expect-error
layers: [],
}),
getMaxZoom: async () => 20,
getMinZoom: async () => 0,
};
},
getTileMapConfig() {
return {
url: 'http://foobar.com/{x}/{y}/{z}',
options: {
minZoom: 0,
maxZoom: 20,
attribution: 'tilemap-attribution',
},
};
},
getDefaultTmsLayer() {
return isUserProvided ? 'TMS in config/kibana.yml' : 'road_map_desaturated';
},
} as unknown as IServiceSettings;
};
let vegaParser: VegaParser;
@ -78,39 +101,10 @@ describe('vega_map_view/view', () => {
setNotifications(coreStart.notifications);
setUISettings(coreStart.uiSettings);
const getTmsService = jest.fn().mockReturnValue({
getVectorStyleSheet: () => ({
version: 8,
sources: {},
// @ts-expect-error
layers: [],
}),
getMaxZoom: async () => 20,
getMinZoom: async () => 0,
getAttributions: () => [{ url: 'tms_attributions' }],
} as unknown as TMSService);
const config = {
tilemap: {
url: 'test',
options: {
attribution: 'tilemap-attribution',
minZoom: 0,
maxZoom: 20,
},
},
} as MapsEmsConfig;
function setMapService(defaultTmsLayer: string) {
setMapServiceSettings({
getTmsService,
defaultTmsLayer: () => defaultTmsLayer,
config,
} as unknown as MapServiceSettings);
}
async function createVegaMapView() {
await vegaParser.parseAsync();
return new VegaMapView({
serviceSettings: await mockGetServiceSettings(),
vegaParser,
filterManager: dataPluginStart.query.filterManager,
timefilter: dataPluginStart.query.timefilter.timefilter,
@ -149,9 +143,8 @@ describe('vega_map_view/view', () => {
});
test('should be added TmsRasterLayer and do not use tmsService if mapStyle is "user_configured"', async () => {
setMapService(userConfiguredLayerId);
isUserProvided = true;
const vegaMapView = await createVegaMapView();
await vegaMapView.init();
const { longitude, latitude, scrollWheelZoom } = vegaMapView._parser.mapConfig;
@ -169,15 +162,13 @@ describe('vega_map_view/view', () => {
scrollZoom: scrollWheelZoom,
center: [longitude, latitude],
});
expect(getTmsService).not.toHaveBeenCalled();
expect(initTmsRasterLayer).toHaveBeenCalled();
expect(initVegaLayer).toHaveBeenCalled();
});
test('should not be added TmsRasterLayer and use tmsService if mapStyle is not "user_configured"', async () => {
setMapService('road_map_desaturated');
isUserProvided = false;
const vegaMapView = await createVegaMapView();
await vegaMapView.init();
const { longitude, latitude, scrollWheelZoom } = vegaMapView._parser.mapConfig;
@ -195,15 +186,12 @@ describe('vega_map_view/view', () => {
scrollZoom: scrollWheelZoom,
center: [longitude, latitude],
});
expect(getTmsService).toHaveBeenCalled();
expect(initTmsRasterLayer).not.toHaveBeenCalled();
expect(initVegaLayer).toHaveBeenCalled();
});
test('should be added NavigationControl', async () => {
setMapService('road_map_desaturated');
const vegaMapView = await createVegaMapView();
await vegaMapView.init();
expect(mapboxgl.NavigationControl).toHaveBeenCalled();

View file

@ -15,18 +15,12 @@ import { mapboxgl } from '@kbn/mapbox-gl';
import { initTmsRasterLayer, initVegaLayer } from './layers';
import { VegaBaseView } from '../vega_base_view';
import { getMapServiceSettings } from '../../services';
import { getAttributionsForTmsService } from './map_service_settings';
import type { MapServiceSettings } from './map_service_settings';
import { getUISettings } from '../../services';
import {
defaultMapConfig,
defaultMabBoxStyle,
userConfiguredLayerId,
vegaLayerId,
} from './constants';
import { defaultMapConfig, defaultMabBoxStyle, vegaLayerId } from './constants';
import { validateZoomSettings, injectMapPropsIntoSpec } from './utils';
import './vega_map_view.scss';
import { TMS_IN_YML_ID } from './service_settings/service_settings_types';
async function updateVegaView(mapBoxInstance: Map, vegaView: View) {
const mapCanvas = mapBoxInstance.getCanvas();
@ -52,17 +46,6 @@ async function updateVegaView(mapBoxInstance: Map, vegaView: View) {
}
export class VegaMapView extends VegaBaseView {
private mapServiceSettings: MapServiceSettings = getMapServiceSettings();
private emsTileLayer = this.getEmsTileLayer();
private getEmsTileLayer() {
const { mapStyle, emsTileServiceId } = this._parser.mapConfig;
if (mapStyle) {
return emsTileServiceId ?? this.mapServiceSettings.defaultTmsLayer();
}
}
private get shouldShowZoomControl() {
return Boolean(this._parser.mapConfig.zoomControl);
}
@ -85,6 +68,17 @@ export class VegaMapView extends VegaBaseView {
};
}
private async getEmsTileLayerId() {
const { mapStyle, emsTileServiceId } = this._parser.mapConfig;
//
if (mapStyle) {
const isDarkMode: boolean = getUISettings().get('theme:darkMode');
return emsTileServiceId
? emsTileServiceId
: await this._serviceSettings.getDefaultTmsLayer(isDarkMode);
}
}
private async initMapContainer(vegaView: View) {
let style: Style = defaultMabBoxStyle;
let customAttribution: MapboxOptions['customAttribution'] = [];
@ -93,24 +87,26 @@ export class VegaMapView extends VegaBaseView {
maxZoom: defaultMapConfig.maxZoom,
};
if (this.emsTileLayer && this.emsTileLayer !== userConfiguredLayerId) {
const tmsService = await this.mapServiceSettings.getTmsService(this.emsTileLayer);
const emsTileLayer = await this.getEmsTileLayerId();
if (emsTileLayer && emsTileLayer !== TMS_IN_YML_ID) {
const tmsService = await this._serviceSettings.getTmsService(emsTileLayer);
if (!tmsService) {
this.onWarn(
i18n.translate('visTypeVega.mapView.mapStyleNotFoundWarningMessage', {
defaultMessage: '{mapStyleParam} was not found',
values: { mapStyleParam: `"emsTileServiceId":${this.emsTileLayer}` },
values: { mapStyleParam: `"emsTileServiceId":${emsTileLayer}` },
})
);
return;
}
zoomSettings.maxZoom = (await tmsService.getMaxZoom()) ?? defaultMapConfig.maxZoom;
zoomSettings.minZoom = (await tmsService.getMinZoom()) ?? defaultMapConfig.minZoom;
customAttribution = getAttributionsForTmsService(tmsService);
zoomSettings.maxZoom = defaultMapConfig.maxZoom;
zoomSettings.minZoom = defaultMapConfig.minZoom;
customAttribution = this._serviceSettings.getAttributionsFromTMSServce(tmsService);
style = (await tmsService.getVectorStyleSheet()) as Style;
} else {
customAttribution = this.mapServiceSettings.config.tilemap.options.attribution;
const config = this._serviceSettings.getTileMapConfig();
customAttribution = config.options.attribution;
}
// In some cases, Vega may be initialized twice, e.g. after awaiting...
@ -127,14 +123,14 @@ export class VegaMapView extends VegaBaseView {
const initMapComponents = () => {
this.initControls(mapBoxInstance);
this.initLayers(mapBoxInstance, vegaView);
this.initLayers(mapBoxInstance, vegaView, emsTileLayer);
this._addDestroyHandler(() => {
if (mapBoxInstance.getLayer(vegaLayerId)) {
mapBoxInstance.removeLayer(vegaLayerId);
}
if (mapBoxInstance.getLayer(userConfiguredLayerId)) {
mapBoxInstance.removeLayer(userConfiguredLayerId);
if (mapBoxInstance.getLayer(TMS_IN_YML_ID)) {
mapBoxInstance.removeLayer(TMS_IN_YML_ID);
}
mapBoxInstance.remove();
});
@ -158,14 +154,13 @@ export class VegaMapView extends VegaBaseView {
mapBoxInstance.touchZoomRotate.disableRotation();
}
private initLayers(mapBoxInstance: Map, vegaView: View) {
const shouldShowUserConfiguredLayer = this.emsTileLayer === userConfiguredLayerId;
private initLayers(mapBoxInstance: Map, vegaView: View, emsTileLayer: string) {
const shouldShowUserConfiguredLayer = emsTileLayer === TMS_IN_YML_ID;
if (shouldShowUserConfiguredLayer) {
const { url, options } = this.mapServiceSettings.config.tilemap;
const { url, options } = this._serviceSettings.getTileMapConfig();
initTmsRasterLayer({
id: userConfiguredLayerId,
id: TMS_IN_YML_ID,
map: mapBoxInstance,
context: {
tiles: [url!],

View file

@ -49,7 +49,7 @@ describe('VegaVisualizations', () => {
mockHeight = jest.spyOn($.prototype, 'height').mockImplementation(() => mockedHeightValue);
};
const mockGetServiceSettings = async () => {
const mockGetServiceSettings = () => {
return {};
};

View file

@ -8,7 +8,6 @@
import { i18n } from '@kbn/i18n';
import { FeatureCollection } from 'geojson';
export const EMS_APP_NAME = 'kibana';
export const MAP_SAVED_OBJECT_TYPE = 'map';
export const APP_ID = 'maps';
export const APP_ICON = 'gisApp';

View file

@ -42,7 +42,6 @@
"kibanaReact",
"kibanaUtils",
"usageCollection",
"home",
"mapsEms"
"home"
]
}

View file

@ -6,21 +6,21 @@
*/
import type { CoreStart } from 'kibana/public';
import type { MapsEmsConfig } from '../../../../src/plugins/maps_ems/public';
import type { MapsConfigType } from '../config';
import type { MapsPluginStartDependencies } from './plugin';
import type { EMSSettings } from '../common/ems_settings';
import type { EMSSettings } from '../../../../src/plugins/maps_ems/common/ems_settings';
import type { PaletteRegistry } from '../../../../src/plugins/charts/public';
let kibanaVersion: string;
export const setKibanaVersion = (version: string) => (kibanaVersion = version);
export const getKibanaVersion = () => kibanaVersion;
import { MapsEmsPluginPublicStart } from '../../../../src/plugins/maps_ems/public';
let coreStart: CoreStart;
let pluginsStart: MapsPluginStartDependencies;
let mapsEms: MapsEmsPluginPublicStart;
let emsSettings: EMSSettings;
export function setStartServices(core: CoreStart, plugins: MapsPluginStartDependencies) {
coreStart = core;
pluginsStart = plugins;
mapsEms = plugins.mapsEms;
emsSettings = mapsEms.createEMSSettings();
}
export const getIndexNameFormComponent = () => pluginsStart.fileUpload.IndexNameFormComponent;
@ -63,25 +63,19 @@ export const getMapAppConfig = () => mapAppConfig;
export const getShowMapsInspectorAdapter = () => getMapAppConfig().showMapsInspectorAdapter;
export const getPreserveDrawingBuffer = () => getMapAppConfig().preserveDrawingBuffer;
// map.* kibana.yml settings from maps_ems plugin that are shared between OSS map visualizations and maps app
let kibanaCommonConfig: MapsEmsConfig;
export const setKibanaCommonConfig = (config: MapsEmsConfig) => (kibanaCommonConfig = config);
export const getKibanaCommonConfig = () => kibanaCommonConfig;
let emsSettings: EMSSettings;
export const setEMSSettings = (value: EMSSettings) => {
emsSettings = value;
export const getMapsEmsStart: () => MapsEmsPluginPublicStart = () => {
return mapsEms;
};
export const getEMSSettings = () => {
export const getEMSSettings: () => EMSSettings = () => {
return emsSettings;
};
export const getEmsTileLayerId = () => getKibanaCommonConfig().emsTileLayerId;
export const getEmsTileLayerId = () => mapsEms.config.emsTileLayerId;
export const getTilemap = () => {
const config = getKibanaCommonConfig();
if (config.tilemap) {
return config.tilemap;
if (mapsEms.config.tilemap) {
return mapsEms.config.tilemap;
} else {
return {};
}

View file

@ -37,10 +37,8 @@ export const LICENCED_FEATURES_DETAILS: Record<LICENSED_FEATURES, LicensedFeatur
let licenseId: string | undefined;
let isGoldPlus: boolean = false;
let isEnterprisePlus: boolean = false;
export const getLicenseId = () => licenseId;
export const getIsGoldPlus = () => isGoldPlus;
export const getIsEnterprisePlus = () => isEnterprisePlus;
let licensingPluginStart: LicensingPluginStart;
let initializeLicense: (value: unknown) => void;
@ -64,10 +62,6 @@ export async function setLicensingPluginStart(licensingPlugin: LicensingPluginSt
function updateLicenseState(license: ILicense) {
const gold = license.check(APP_ID, 'gold');
isGoldPlus = gold.state === 'valid';
const enterprise = license.check(APP_ID, 'enterprise');
isEnterprisePlus = enterprise.state === 'valid';
licenseId = license.uid;
}

View file

@ -21,13 +21,7 @@ import type {
} from '../../../../src/core/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { MapInspectorView } from './inspector/map_inspector_view';
import {
setEMSSettings,
setKibanaCommonConfig,
setKibanaVersion,
setMapAppConfig,
setStartServices,
} from './kibana_services';
import { setMapAppConfig, setStartServices } from './kibana_services';
import { featureCatalogueEntry } from './feature_catalogue_entry';
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
import type { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
@ -55,18 +49,13 @@ import {
import { registerLayerWizard } from './classes/layers';
import { registerSource } from './classes/sources/source_registry';
import type { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
import type { MapsEmsPluginSetup } from '../../../../src/plugins/maps_ems/public';
import type { MapsEmsPluginPublicStart } from '../../../../src/plugins/maps_ems/public';
import type { DataPublicPluginStart } from '../../../../src/plugins/data/public';
import type { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public';
import type { FileUploadPluginStart } from '../../file_upload/public';
import type { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public';
import type { PresentationUtilPluginStart } from '../../../../src/plugins/presentation_util/public';
import {
getIsEnterprisePlus,
registerLicensedFeatures,
setLicensingPluginStart,
} from './licensed_features';
import { EMSSettings } from '../common/ems_settings';
import { registerLicensedFeatures, setLicensingPluginStart } from './licensed_features';
import type { SavedObjectTaggingPluginStart } from '../../saved_objects_tagging/public';
import type { ChartsPluginStart } from '../../../../src/plugins/charts/public';
import {
@ -91,7 +80,6 @@ export interface MapsPluginSetupDependencies {
home?: HomePublicPluginSetup;
visualizations: VisualizationsSetup;
embeddable: EmbeddableSetup;
mapsEms: MapsEmsPluginSetup;
share: SharePluginSetup;
licensing: LicensingPluginSetup;
usageCollection?: UsageCollectionSetup;
@ -114,6 +102,7 @@ export interface MapsPluginStartDependencies {
presentationUtil: PresentationUtilPluginStart;
security?: SecurityPluginStart;
spaces?: SpacesPluginStart;
mapsEms: MapsEmsPluginPublicStart;
}
/**
@ -144,12 +133,7 @@ export class MapsPlugin
registerLicensedFeatures(plugins.licensing);
const config = this._initializerContext.config.get<MapsConfigType>();
setKibanaCommonConfig(plugins.mapsEms.config);
setMapAppConfig(config);
setKibanaVersion(this._initializerContext.env.packageInfo.version);
const emsSettings = new EMSSettings(plugins.mapsEms.config, getIsEnterprisePlus);
setEMSSettings(emsSettings);
const locator = plugins.share.url.locators.create(
new MapsAppLocatorDefinition({

View file

@ -5,40 +5,12 @@
* 2.0.
*/
import { EMSClient } from '@elastic/ems-client';
import { getEMSClient, getGlyphUrl } from './util';
import { getGlyphUrl } from './util';
jest.mock('@elastic/ems-client');
const EMS_FONTS_URL_MOCK = 'ems/fonts';
const MOCK_EMS_SETTINGS = {
isEMSEnabled: () => true,
getEMSFileApiUrl: () => 'https://file-api',
getEMSTileApiUrl: () => 'https://tile-api',
getEMSLandingPageUrl: () => 'http://test.com',
getEMSFontLibraryUrl: () => EMS_FONTS_URL_MOCK,
isProxyElasticMapsServiceInMaps: () => false,
};
describe('default use without proxy', () => {
beforeEach(() => {
require('./kibana_services').getEmsTileLayerId = () => '123';
require('./kibana_services').getEMSSettings = () => {
return MOCK_EMS_SETTINGS;
};
require('./licensed_features').getLicenseId = () => {
return 'foobarlicenseid';
};
});
test('should construct EMSClient with absolute file and tile API urls', async () => {
getEMSClient();
const mockEmsClientCall = EMSClient.mock.calls[0];
expect(mockEmsClientCall[0].fileApiUrl.startsWith('https://file-api')).toBe(true);
expect(mockEmsClientCall[0].tileApiUrl.startsWith('https://tile-api')).toBe(true);
});
});
describe('getGlyphUrl', () => {
describe('EMS enabled', () => {
beforeAll(() => {
@ -53,13 +25,18 @@ describe('getGlyphUrl', () => {
beforeAll(() => {
require('./kibana_services').getEMSSettings = () => {
return {
...MOCK_EMS_SETTINGS,
getEMSFontLibraryUrl() {
return 'foobar';
},
isEMSEnabled() {
return true;
},
};
};
});
test('should return EMS fonts URL', async () => {
expect(getGlyphUrl()).toBe(EMS_FONTS_URL_MOCK);
expect(getGlyphUrl()).toBe('foobar');
});
});
});

View file

@ -5,10 +5,9 @@
* 2.0.
*/
import { i18n } from '@kbn/i18n';
import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client';
import { EMS_APP_NAME, FONTS_API_PATH } from '../common/constants';
import { getHttp, getTilemap, getKibanaVersion, getEMSSettings } from './kibana_services';
import { FONTS_API_PATH } from '../common/constants';
import { getHttp, getTilemap, getEMSSettings, getMapsEmsStart } from './kibana_services';
import { getLicenseId } from './licensed_features';
export function getKibanaTileMap(): unknown {
@ -20,7 +19,7 @@ export async function getEmsFileLayers(): Promise<FileLayer[]> {
return [];
}
return getEMSClient().getFileLayers();
return (await getEMSClient()).getFileLayers();
}
export async function getEmsTmsServices(): Promise<TMSService[]> {
@ -28,30 +27,14 @@ export async function getEmsTmsServices(): Promise<TMSService[]> {
return [];
}
return getEMSClient().getTMSServices();
return (await getEMSClient()).getTMSServices();
}
let emsClient: EMSClient | null = null;
let latestLicenseId: string | undefined;
export function getEMSClient(): EMSClient {
async function getEMSClient(): Promise<EMSClient> {
if (!emsClient) {
const emsSettings = getEMSSettings();
const proxyPath = '';
const tileApiUrl = emsSettings!.getEMSTileApiUrl();
const fileApiUrl = emsSettings!.getEMSFileApiUrl();
emsClient = new EMSClient({
language: i18n.getLocale(),
appVersion: getKibanaVersion(),
appName: EMS_APP_NAME,
tileApiUrl,
fileApiUrl,
landingPageUrl: emsSettings!.getEMSLandingPageUrl(),
fetchFunction(url: string) {
return fetch(url);
},
proxyPath,
});
emsClient = await getMapsEmsStart().createEMSClient();
}
const licenseId = getLicenseId();
if (latestLicenseId !== licenseId) {

View file

@ -6,7 +6,7 @@
*/
import { CoreStart } from '../../../../src/core/server';
import { StartDeps } from './plugin';
import { StartDeps } from './types';
let coreStart: CoreStart;
let pluginsStart: StartDeps;

View file

@ -8,7 +8,9 @@
import _ from 'lodash';
import { DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../common/constants';
export function getIndexPatternSettings(indicesSettingsResp) {
export function getIndexPatternSettings(
indicesSettingsResp: Record<string, string | number | boolean>
) {
let maxResultWindow = Infinity;
let maxInnerResultWindow = Infinity;
Object.values(indicesSettingsResp).forEach((indexSettings) => {

View file

@ -14,7 +14,6 @@ import {
PluginInitializerContext,
DEFAULT_APP_CATEGORIES,
} from '../../../../src/core/server';
import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server';
// @ts-ignore
import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects';
// @ts-ignore
@ -26,44 +25,21 @@ import { APP_ID, APP_ICON, MAP_SAVED_OBJECT_TYPE, getFullPath } from '../common/
import { mapSavedObjects, mapsTelemetrySavedObjects } from './saved_objects';
import { MapsXPackConfig } from '../config';
import { setStartServices } from './kibana_server_services';
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server';
import { emsBoundariesSpecProvider } from './tutorials/ems';
// @ts-ignore
import { initRoutes } from './routes';
import { ILicense } from '../../licensing/common/types';
import { LicensingPluginSetup } from '../../licensing/server';
import { HomeServerPluginSetup } from '../../../../src/plugins/home/server';
import { MapsEmsPluginSetup } from '../../../../src/plugins/maps_ems/server';
import { EMSSettings } from '../common/ems_settings';
import { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server';
import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server';
import type { EMSSettings } from '../../../../src/plugins/maps_ems/server';
import { embeddableMigrations } from './embeddable_migrations';
import { CustomIntegrationsPluginSetup } from '../../../../src/plugins/custom_integrations/server';
import { registerIntegrations } from './register_integrations';
interface SetupDeps {
features: FeaturesPluginSetupContract;
usageCollection?: UsageCollectionSetup;
home?: HomeServerPluginSetup;
licensing: LicensingPluginSetup;
mapsEms: MapsEmsPluginSetup;
embeddable: EmbeddableSetup;
customIntegrations?: CustomIntegrationsPluginSetup;
}
export interface StartDeps {
data: DataPluginStart;
}
import { StartDeps, SetupDeps } from './types';
export class MapsPlugin implements Plugin {
readonly _initializerContext: PluginInitializerContext<MapsXPackConfig>;
private readonly _logger: Logger;
private readonly kibanaVersion: string;
constructor(initializerContext: PluginInitializerContext<MapsXPackConfig>) {
this._logger = initializerContext.logger.get();
this._initializerContext = initializerContext;
this.kibanaVersion = initializerContext.env.packageInfo.version;
}
_initHomeData(
@ -169,20 +145,12 @@ export class MapsPlugin implements Plugin {
}
setup(core: CoreSetup, plugins: SetupDeps) {
const { usageCollection, home, licensing, features, mapsEms, customIntegrations } = plugins;
const mapsEmsConfig = mapsEms.config;
const { usageCollection, home, features, customIntegrations } = plugins;
const config$ = this._initializerContext.config.create();
let isEnterprisePlus = false;
let lastLicenseId: string | undefined;
const emsSettings = new EMSSettings(mapsEmsConfig, () => isEnterprisePlus);
licensing.license$.subscribe((license: ILicense) => {
const enterprise = license.check(APP_ID, 'enterprise');
isEnterprisePlus = enterprise.state === 'valid';
lastLicenseId = license.uid;
});
const emsSettings = plugins.mapsEms.createEMSSettings();
initRoutes(core, () => lastLicenseId, emsSettings, this.kibanaVersion, this._logger);
initRoutes(core, this._logger);
if (home) {
this._initHomeData(home, core.http.basePath.prepend, emsSettings);

View file

@ -5,17 +5,21 @@
* 2.0.
*/
import { INDEX_SETTINGS_API_PATH, FONTS_API_PATH } from '../common/constants';
import { getIndexPatternSettings } from './lib/get_index_pattern_settings';
import { schema } from '@kbn/config-schema';
import fs from 'fs';
import path from 'path';
import { CoreSetup, IRouter, Logger } from 'kibana/server';
import { INDEX_SETTINGS_API_PATH, FONTS_API_PATH } from '../common/constants';
import { getIndexPatternSettings } from './lib/get_index_pattern_settings';
import { initMVTRoutes } from './mvt/mvt_routes';
import { initIndexingRoutes } from './data_indexing/indexing_routes';
import { StartDeps, SetupDeps } from './types';
import { DataRequestHandlerContext } from '../../../../src/plugins/data/server';
export async function initRoutes(core, getLicenseId, emsSettings, kbnVersion, logger) {
const router = core.http.createRouter();
const [, { data: dataPlugin }] = await core.getStartServices();
export async function initRoutes(core: CoreSetup, logger: Logger): Promise<void> {
const router: IRouter<DataRequestHandlerContext> = core.http.createRouter();
const [, { data: dataPlugin }]: [SetupDeps, StartDeps] =
(await core.getStartServices()) as unknown as [SetupDeps, StartDeps];
router.get(
{
@ -72,7 +76,9 @@ export async function initRoutes(core, getLicenseId, emsSettings, kbnVersion, lo
const resp = await context.core.elasticsearch.client.asCurrentUser.indices.getSettings({
index: query.indexPatternTitle,
});
const indexPatternSettings = getIndexPatternSettings(resp.body);
const indexPatternSettings = getIndexPatternSettings(
resp.body as unknown as Record<string, string | number | boolean>
);
return response.ok({
body: indexPatternSettings,
});
@ -80,7 +86,7 @@ export async function initRoutes(core, getLicenseId, emsSettings, kbnVersion, lo
logger.warn(
`Cannot load index settings for data view '${query.indexPatternTitle}', error: ${error.message}.`
);
response.custom({
return response.custom({
body: 'Error loading index settings',
statusCode: 400,
});

View file

@ -0,0 +1,29 @@
/*
* 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 { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server';
import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server';
import { HomeServerPluginSetup } from '../../../../src/plugins/home/server';
import { LicensingPluginSetup } from '../../licensing/server';
import { MapsEmsPluginServerSetup } from '../../../../src/plugins/maps_ems/server';
import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server';
import { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server';
import { CustomIntegrationsPluginSetup } from '../../../../src/plugins/custom_integrations/server';
export interface SetupDeps {
features: FeaturesPluginSetupContract;
usageCollection?: UsageCollectionSetup;
home?: HomeServerPluginSetup;
licensing: LicensingPluginSetup;
mapsEms: MapsEmsPluginServerSetup;
embeddable: EmbeddableSetup;
customIntegrations: CustomIntegrationsPluginSetup;
}
export interface StartDeps {
data: DataPluginStart;
}