diff --git a/src/core_plugins/spy_modes/public/req_resp_stats_spy_mode.html b/src/core_plugins/spy_modes/public/req_resp_stats_spy_mode.html index c4a97803d2db..37321e173737 100644 --- a/src/core_plugins/spy_modes/public/req_resp_stats_spy_mode.html +++ b/src/core_plugins/spy_modes/public/req_resp_stats_spy_mode.html @@ -14,7 +14,7 @@ -
{{req.fetchParams.body | json}}
+
{{req.fetchParams.body | json}}
diff --git a/src/core_plugins/tile_map/public/kibana_map.js b/src/core_plugins/tile_map/public/kibana_map.js index 958b16e0ca5b..a9959daee390 100644 --- a/src/core_plugins/tile_map/public/kibana_map.js +++ b/src/core_plugins/tile_map/public/kibana_map.js @@ -663,7 +663,7 @@ export class KibanaMap extends EventEmitter { if (!centerFromUIState || centerFromMap.lon !== centerFromUIState[1] || centerFromMap.lat !== centerFromUIState[0]) { visualization.uiStateVal('mapCenter', [centerFromMap.lat, centerFromMap.lon]); } - uiState.set('mapBounds', this.getUntrimmedBounds()); + visualization.sessionState.mapBounds = this.getUntrimmedBounds(); } this.on('dragend', persistMapStateInUiState); diff --git a/src/core_plugins/tile_map/public/maps_visualization.js b/src/core_plugins/tile_map/public/maps_visualization.js index 53a1b7746c06..4694afb1ad06 100644 --- a/src/core_plugins/tile_map/public/maps_visualization.js +++ b/src/core_plugins/tile_map/public/maps_visualization.js @@ -20,13 +20,11 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState class MapsVisualization { constructor(element, vis) { - this.vis = vis; this.$el = $(element); this._$container = this.$el; this._geohashLayer = null; this._kibanaMap = null; - this._kibanaMapReady = this._makeKibanaMap(); this._baseLayerDirty = true; this._currentParams = null; } @@ -37,6 +35,11 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState } } + init() { + this._kibanaMapReady = this._makeKibanaMap(); + return this._kibanaMapReady; + } + async render(esResponse, status) { return new Promise(async(resolve) => { @@ -66,7 +69,6 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState //********************************************************************************************************** async _makeKibanaMap() { - try { this._tmsService = await serviceSettings.getTMSService(); this._tmsError = null; @@ -88,8 +90,8 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState options.center = centerFromUIState ? centerFromUIState : this.vis.type.visConfig.defaults.mapCenter; this._kibanaMap = new KibanaMap(containerElement, options); - uiState.set('mapZoom', this._kibanaMap.getZoomLevel()); - uiState.set('mapBounds', this._kibanaMap.getUntrimmedBounds()); + this.vis.sessionState.mapBounds = this._kibanaMap.getUntrimmedBounds(); + this._kibanaMap.addDrawControl(); this._kibanaMap.addFitControl(); this._kibanaMap.addLegendControl(); @@ -170,7 +172,6 @@ export function MapsVisualizationProvider(serviceSettings, Notifier, getAppState * called on options change (vis.params change) */ async _updateParams() { - const mapParams = this._getMapsParams(); if (_.eq(this._currentParams, mapParams)) { return; diff --git a/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js index 4f5928689356..f4e89091c143 100644 --- a/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js +++ b/src/ui/public/agg_types/__tests__/buckets/_geo_hash.js @@ -1,17 +1,48 @@ import expect from 'expect.js'; import { AggTypesBucketsGeoHashProvider } from 'ui/agg_types/buckets/geo_hash'; -describe('Geohash Agg', function () { +describe('Geohash Agg', () => { - // Mock BucketAggType that does not create an AggType, but instead returns the AggType parameters + const intialZoom = 10; + const initialMapBounds = { + top_left: { lat: 1.0, lon: -1.0 }, + bottom_right: { lat: -1.0, lon: 1.0 } + }; + const aggMock = { + getField: () => { + return { + name: 'location' + }; + }, + params: { + isFilteredByCollar: true, + useGeocentroid: true + }, + vis: { + hasUiState: () => { + return false; + }, + params: { + mapZoom: intialZoom + }, + sessionState: {} + }, + type: 'geohash_grid', + }; const BucketAggTypeMock = (aggOptions) => { - return aggOptions.params; + return aggOptions; + }; + const AggConfigMock = (vis, aggOptions) => { + return aggOptions; }; const PrivateMock = (provider) => { switch (provider.name) { case 'AggTypesBucketsBucketAggTypeProvider': return BucketAggTypeMock; break; + case 'VisAggConfigProvider': + return AggConfigMock; + break; default: return () => {}; } @@ -21,18 +52,54 @@ describe('Geohash Agg', function () { return 7;//"visualization:tileMap:maxPrecision" } }; - const params = AggTypesBucketsGeoHashProvider(PrivateMock, configMock); // eslint-disable-line new-cap - describe('precision parameter', function () { + function initVisSessionState() { + aggMock.vis.sessionState = { + mapBounds: initialMapBounds + }; + } + + function initAggParams() { + aggMock.params.isFilteredByCollar = true; + aggMock.params.useGeocentroid = true; + } + + function zoomMap(zoomChange) { + aggMock.vis.params.mapZoom += zoomChange; + } + + function moveMap(newBounds) { + aggMock.vis.sessionState.mapBounds = newBounds; + } + + function resetMap() { + aggMock.vis.params.mapZoom = intialZoom; + aggMock.vis.sessionState.mapBounds = initialMapBounds; + aggMock.vis.sessionState.mapCollar = { + top_left: { lat: 1.5, lon: -1.5 }, + bottom_right: { lat: -1.5, lon: 1.5 }, + zoom: intialZoom + }; + } + + let geohashAgg; + beforeEach(() => { + geohashAgg = AggTypesBucketsGeoHashProvider(PrivateMock, configMock); // eslint-disable-line new-cap + }); + + describe('precision parameter', () => { const PRECISION_PARAM_INDEX = 6; - const precisionParam = params[PRECISION_PARAM_INDEX]; + let precisionParam; + beforeEach(() => { + precisionParam = geohashAgg.params[PRECISION_PARAM_INDEX]; + }); it('should select precision parameter', () => { expect(precisionParam.name).to.equal('precision'); }); - describe('precision parameter write', function () { + describe('precision parameter write', () => { const zoomToGeoHashPrecision = { 0: 1, @@ -77,4 +144,115 @@ describe('Geohash Agg', function () { }); }); + + describe('getRequestAggs', () => { + + describe('initial aggregation creation', () => { + let requestAggs; + beforeEach(() => { + initVisSessionState(); + initAggParams(); + requestAggs = geohashAgg.getRequestAggs(aggMock); + }); + + it('should create filter, geohash_grid, and geo_centroid aggregations', () => { + expect(requestAggs.length).to.equal(3); + expect(requestAggs[0].type).to.equal('filter'); + expect(requestAggs[1].type).to.equal('geohash_grid'); + expect(requestAggs[2].type).to.equal('geo_centroid'); + }); + + it('should set mapCollar in vis session state', () => { + expect(aggMock.vis.sessionState).to.have.property('mapCollar'); + expect(aggMock.vis.sessionState.mapCollar).to.have.property('top_left'); + expect(aggMock.vis.sessionState.mapCollar).to.have.property('bottom_right'); + expect(aggMock.vis.sessionState.mapCollar).to.have.property('zoom'); + }); + + // there was a bug because of an "&& mapZoom" check which excluded 0 as a valid mapZoom, but it is. + it('should create filter, geohash_grid, and geo_centroid aggregations when zoom level 0', () => { + aggMock.vis.params.mapZoom = 0; + requestAggs = geohashAgg.getRequestAggs(aggMock); + expect(requestAggs.length).to.equal(3); + expect(requestAggs[0].type).to.equal('filter'); + expect(requestAggs[1].type).to.equal('geohash_grid'); + expect(requestAggs[2].type).to.equal('geo_centroid'); + }); + }); + + describe('aggregation options', () => { + + beforeEach(() => { + initVisSessionState(); + initAggParams(); + }); + + it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { + aggMock.params.isFilteredByCollar = false; + const requestAggs = geohashAgg.getRequestAggs(aggMock); + expect(requestAggs.length).to.equal(2); + expect(requestAggs[0].type).to.equal('geohash_grid'); + expect(requestAggs[1].type).to.equal('geo_centroid'); + }); + + it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { + aggMock.params.useGeocentroid = false; + const requestAggs = geohashAgg.getRequestAggs(aggMock); + expect(requestAggs.length).to.equal(2); + expect(requestAggs[0].type).to.equal('filter'); + expect(requestAggs[1].type).to.equal('geohash_grid'); + + }); + }); + + describe('aggregation creation after map interaction', () => { + + let origRequestAggs; + let origMapCollar; + beforeEach(() => { + resetMap(); + initAggParams(); + origRequestAggs = geohashAgg.getRequestAggs(aggMock); + origMapCollar = aggMock.vis.sessionState.mapCollar; + }); + + it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { + moveMap({ + top_left: { lat: 1.1, lon: -1.1 }, + bottom_right: { lat: -0.9, lon: 0.9 } + }); + + const newRequestAggs = geohashAgg.getRequestAggs(aggMock); + expect(JSON.stringify(origRequestAggs[0].params, null, '')).to.equal(JSON.stringify(newRequestAggs[0].params, null, '')); + + const newMapCollar = aggMock.vis.sessionState.mapCollar; + expect(JSON.stringify(origMapCollar, null, '')).to.equal(JSON.stringify(newMapCollar, null, '')); + }); + + it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { + moveMap({ + top_left: { lat: 10.0, lon: -10.0 }, + bottom_right: { lat: 9.0, lon: -9.0 } + }); + + const newRequestAggs = geohashAgg.getRequestAggs(aggMock); + expect(JSON.stringify(origRequestAggs[0].params, null, '')).not.to.equal(JSON.stringify(newRequestAggs[0].params, null, '')); + + const newMapCollar = aggMock.vis.sessionState.mapCollar; + expect(JSON.stringify(origMapCollar, null, '')).not.to.equal(JSON.stringify(newMapCollar, null, '')); + }); + + it('should change geo_bounding_box filter aggregation and vis session state when map zoom level changes', () => { + zoomMap(-1); + + const newRequestAggs = geohashAgg.getRequestAggs(aggMock); + expect(JSON.stringify(origRequestAggs[0].params, null, '')).not.to.equal(JSON.stringify(newRequestAggs[0].params, null, '')); + + const newMapCollar = aggMock.vis.sessionState.mapCollar; + expect(JSON.stringify(origMapCollar, null, '')).not.to.equal(JSON.stringify(newMapCollar, null, '')); + }); + + }); + + }); }); diff --git a/src/ui/public/agg_types/buckets/geo_hash.js b/src/ui/public/agg_types/buckets/geo_hash.js index 231f1c472ef5..cfdda6cd002e 100644 --- a/src/ui/public/agg_types/buckets/geo_hash.js +++ b/src/ui/public/agg_types/buckets/geo_hash.js @@ -45,6 +45,14 @@ export function AggTypesBucketsGeoHashProvider(Private, config) { return precision; } + function getMapZoom(vis) { + if (vis.hasUiState() && parseInt(vis.uiStateVal('mapZoom')) >= 0) { + return parseInt(vis.uiStateVal('mapZoom')); + } + + return vis.params.mapZoom; + } + function isOutsideCollar(bounds, collar) { return bounds && collar && !geoContains(collar, bounds); } @@ -89,11 +97,8 @@ export function AggTypesBucketsGeoHashProvider(Private, config) { }, write: function (aggConfig, output) { const vis = aggConfig.vis; - let currZoom; - if (vis.hasUiState()) { - currZoom = parseInt(vis.uiStateVal('mapZoom'), 10); - } - const autoPrecisionVal = zoomPrecision[currZoom >= 0 ? currZoom : parseInt(vis.params.mapZoom)]; + const currZoom = getMapZoom(vis); + const autoPrecisionVal = zoomPrecision[currZoom]; output.params.precision = aggConfig.params.autoPrecision ? autoPrecisionVal : getPrecision(aggConfig.params.precision); } } @@ -103,25 +108,23 @@ export function AggTypesBucketsGeoHashProvider(Private, config) { if (agg.params.isFilteredByCollar && agg.getField()) { const vis = agg.vis; - let mapBounds; - let mapZoom; - if (vis.hasUiState()) { - mapBounds = vis.uiStateVal('mapBounds'); - mapZoom = vis.uiStateVal('mapZoom'); - } - if (mapBounds && mapZoom) { - const lastMapCollar = vis.uiStateVal('mapCollar'); + const mapBounds = vis.sessionState.mapBounds; + const mapZoom = getMapZoom(vis); + if (mapBounds) { + const lastMapCollar = vis.sessionState.mapCollar; let mapCollar; if (!lastMapCollar || lastMapCollar.zoom !== mapZoom || isOutsideCollar(mapBounds, lastMapCollar)) { mapCollar = scaleBounds(mapBounds); mapCollar.zoom = mapZoom; - vis.getUiState().set('mapCollar', mapCollar); + vis.sessionState.mapCollar = mapCollar; } else { mapCollar = lastMapCollar; } const boundingBox = {}; - delete mapCollar.zoom; // zoom is not part of bounding box filter - boundingBox[agg.getField().name] = mapCollar; + boundingBox[agg.getField().name] = { + top_left: mapCollar.top_left, + bottom_right: mapCollar.bottom_right + }; aggs.push(new AggConfig(agg.vis, { type: 'filter', id: 'filter_agg', diff --git a/src/ui/public/agg_types/controls/precision.html b/src/ui/public/agg_types/controls/precision.html index 5a83f224aa51..c567f3d56593 100644 --- a/src/ui/public/agg_types/controls/precision.html +++ b/src/ui/public/agg_types/controls/precision.html @@ -40,6 +40,7 @@