[Maps] update layers on kibana global refresh interval (#27721)

* update layers on kibana global refresh interval

* store refresh in UI state

* clearer function names for refresh timer

* rename a bunch of things

* fix store_actions jest test

* add functional test to verify refresh config is loaded from saved object state

* add functional tests verifing layers re-fetch data on refresh timer
This commit is contained in:
Nathan Reese 2018-12-26 10:14:43 -07:00 committed by GitHub
parent 9f6bd394a8
commit 72680f2fa0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 335 additions and 122 deletions

View file

@ -25,6 +25,7 @@
ng-class="{'kuiLocalMenuItem-isSelected': kbnTopNav.isCurrent('interval') }"
ng-show="timefilterValues.isAutoRefreshSelectorEnabled"
ng-click="kbnTopNav.toggle('interval')"
data-test-subj="globalRefreshButton"
>
<span ng-show="timefilterValues.refreshInterval.value === 0">
<span aria-hidden="true" class="kuiIcon fa-repeat"></span> Auto-refresh

View file

@ -194,6 +194,12 @@ export function HeaderPageProvider({ getService, getPageObjects }) {
return testSubjects.getAttribute('globalTimepickerAutoRefreshButton', 'data-test-subj-state');
}
async getRefreshConfig() {
const refreshState = await testSubjects.getAttribute('globalTimepickerAutoRefreshButton', 'data-test-subj-state');
const refreshConfig = await testSubjects.getVisibleText('globalRefreshButton');
return `${refreshState} ${refreshConfig}`;
}
// check if the auto refresh state is active and to pause it
async pauseAutoRefresh() {
let result = false;

View file

@ -9,6 +9,7 @@ import turfBooleanContains from '@turf/boolean-contains';
import { GIS_API_PATH } from '../../common/constants';
import { getLayerList, getLayerListRaw, getDataFilters, getSelectedLayer } from '../selectors/map_selectors';
import { timeService } from '../kibana_services';
export const SET_SELECTED_LAYER = 'SET_SELECTED_LAYER';
export const UPDATE_LAYER_ORDER = 'UPDATE_LAYER_ORDER';
@ -27,6 +28,7 @@ export const LAYER_DATA_LOAD_ERROR = 'LAYER_DATA_LOAD_ERROR';
export const REPLACE_LAYERLIST = 'REPLACE_LAYERLIST';
export const SET_JOINS = 'SET_JOINS';
export const SET_TIME_FILTERS = 'SET_TIME_FILTERS';
export const TRIGGER_REFRESH_TIMER = 'TRIGGER_REFRESH_TIMER';
export const UPDATE_LAYER_PROP = 'UPDATE_LAYER_PROP';
export const UPDATE_LAYER_STYLE_FOR_SELECTED_LAYER = 'UPDATE_LAYER_STYLE';
export const PROMOTE_TEMPORARY_STYLES = 'PROMOTE_TEMPORARY_STYLES';
@ -34,6 +36,7 @@ export const CLEAR_TEMPORARY_STYLES = 'CLEAR_TEMPORARY_STYLES';
export const TOUCH_LAYER = 'TOUCH_LAYER';
export const UPDATE_LAYER_ALPHA_VALUE = 'UPDATE_LAYER_ALPHA_VALUE';
export const UPDATE_SOURCE_PROP = 'UPDATE_SOURCE_PROP';
export const SET_REFRESH_CONFIG = 'SET_REFRESH_CONFIG';
const GIS_API_RELATIVE = `../${GIS_API_PATH}`;
@ -332,16 +335,58 @@ export function setMeta(metaJson) {
};
}
export function setTimeFilters(timeFilters) {
export function setTimeFiltersToKbnGlobalTime() {
return (dispatch) => {
dispatch(setTimeFilters(timeService.getTime()));
};
}
export function setTimeFilters({ from, to }) {
return async (dispatch, getState) => {
dispatch({
type: SET_TIME_FILTERS,
...timeFilters
from,
to,
});
// Update Kibana global time
const kbnTime = timeService.getTime();
if ((to && to !== kbnTime.to) || (from && from !== kbnTime.from)) {
timeService.setTime({ from, to });
}
const dataFilters = getDataFilters(getState());
await syncDataForAllLayers(getState, dispatch, dataFilters);
};
}
export function setRefreshConfig({ isPaused, interval }) {
return async (dispatch) => {
dispatch({
type: SET_REFRESH_CONFIG,
isPaused,
interval,
});
// Update Kibana global refresh
const kbnRefresh = timeService.getRefreshInterval();
if (isPaused !== kbnRefresh.pause || interval !== kbnRefresh.value) {
timeService.setRefreshInterval({
pause: isPaused,
value: interval,
});
}
};
}
export function triggerRefreshTimer() {
return async (dispatch, getState) => {
dispatch({
type: TRIGGER_REFRESH_TIMER,
});
const dataFilters = getDataFilters(getState());
const newDataFilters = { ...dataFilters, timeFilters: { ...timeFilters } };
await syncDataForAllLayers(getState, dispatch, newDataFilters);
await syncDataForAllLayers(getState, dispatch, dataFilters);
};
}

View file

@ -5,6 +5,7 @@
*/
jest.mock('../selectors/map_selectors', () => ({}));
jest.mock('../kibana_services', () => ({}));
import { mapExtentChanged } from './store_actions';

View file

@ -13,7 +13,13 @@ import { timefilter } from 'ui/timefilter';
import { Provider } from 'react-redux';
import { getStore } from '../store/store';
import { GisMap } from '../components/gis_map';
import { setSelectedLayer, setTimeFilters, mapExtentChanged, replaceLayerList } from '../actions/store_actions';
import {
setSelectedLayer,
setTimeFilters,
setRefreshConfig,
mapExtentChanged,
replaceLayerList,
} from '../actions/store_actions';
import { getIsDarkTheme, updateFlyout, FLYOUT_STATE } from '../store/ui';
import { Inspector } from 'ui/inspector';
import { inspectorAdapters } from '../kibana_services';
@ -21,7 +27,7 @@ import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_s
import { showSaveModal } from 'ui/saved_objects/show_saved_object_save_modal';
import { showOptionsPopover } from '../components/top_nav/show_options_popover';
import { toastNotifications } from 'ui/notify';
import { getMapReady, getTimeFilters } from "../selectors/map_selectors";
import { getMapReady } from "../selectors/map_selectors";
const REACT_ANCHOR_DOM_ELEMENT_ID = 'react-gis-root';
@ -57,6 +63,9 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl) => {
zoom: mapState.zoom,
center: mapState.center,
}));
if (mapState.refreshConfig) {
store.dispatch(setRefreshConfig(mapState.refreshConfig));
}
}
const root = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID);
@ -73,12 +82,6 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl) => {
updateTheme();
}
const storeTime = getTimeFilters(store.getState());
const kbnTime = timefilter.getTime();
if (storeTime && (storeTime.to !== kbnTime.to || storeTime.from !== kbnTime.from)) {
timefilter.setTime(storeTime);
}
// Part of initial syncing of store from saved object
// Delayed until after map is ready so map extent is known
if (!isLayersListInitializedFromSavedObject && getMapReady(store.getState())) {
@ -88,13 +91,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl) => {
}
}
timefilter.on('timeUpdate', dispatchTimeUpdate);
$scope.$on('$destroy', () => {
if (unsubscribe) {
unsubscribe();
}
timefilter.off('timeUpdate', dispatchTimeUpdate);
const node = document.getElementById(REACT_ANCHOR_DOM_ELEMENT_ID);
if (node) {
unmountComponentAtNode(node);
@ -192,12 +192,6 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl) => {
timefilter.enableTimeRangeSelector();
timefilter.enableAutoRefreshSelector();
async function dispatchTimeUpdate() {
const timeFilters = timefilter.getTime();
const store = await getStore();
store.dispatch(setTimeFilters(timeFilters));
}
function updateTheme() {
$scope.$evalAsync(() => {
isDarkTheme ? setDarkTheme() : setLightTheme();

View file

@ -13,6 +13,7 @@ import {
getMapCenter,
getLayerListRaw,
getMapExtent,
getRefreshConfig,
} from '../../selectors/map_selectors';
import { getIsDarkTheme } from '../../store/ui';
import { TileStyle } from '../../shared/layers/styles/tile_style';
@ -95,6 +96,7 @@ module.factory('SavedGisMap', function (Private) {
zoom: getMapZoom(state),
center: getMapCenter(state),
timeFilters: getTimeFilters(state),
refreshConfig: getRefreshConfig(state),
});
this.uiStateJSON = JSON.stringify({

View file

@ -7,6 +7,11 @@
import { connect } from 'react-redux';
import { GisMap } from './view';
import { getFlyoutDisplay, FLYOUT_STATE } from '../../store/ui';
import {
setTimeFiltersToKbnGlobalTime,
triggerRefreshTimer,
setRefreshConfig
} from '../../actions/store_actions';
function mapStateToProps(state = {}) {
const flyoutDisplay = getFlyoutDisplay(state);
@ -17,5 +22,13 @@ function mapStateToProps(state = {}) {
};
}
const connectedGisMap = connect(mapStateToProps)(GisMap);
function mapDispatchToProps(dispatch) {
return {
setTimeFiltersToKbnGlobalTime: () => dispatch(setTimeFiltersToKbnGlobalTime()),
triggerRefreshTimer: () => dispatch(triggerRefreshTimer()),
setRefreshConfig: (({ isPaused, interval }) => dispatch(setRefreshConfig({ isPaused, interval }))),
};
}
const connectedGisMap = connect(mapStateToProps, mapDispatchToProps)(GisMap);
export { connectedGisMap as GisMap };

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import React, { Component } from 'react';
import { MBMapContainer } from '../map/mb';
import { LayerControl } from '../layer_control/index';
import { LayerPanel } from '../layer_panel/index';
@ -12,40 +12,81 @@ import { AddLayerPanel } from '../layer_addpanel/index';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { Toasts } from '../toasts';
import { timeService } from '../../kibana_services';
export function GisMap(props) {
const {
layerDetailsVisible,
addLayerVisible,
noFlyoutVisible
} = props;
export class GisMap extends Component {
let currentPanel;
let currentPanelClassName;
componentDidMount() {
timeService.on('timeUpdate', this.props.setTimeFiltersToKbnGlobalTime);
timeService.on('refreshIntervalUpdate', this.setRefreshTimer);
this.setRefreshTimer();
}
if (noFlyoutVisible) {
currentPanel = null;
} else if (addLayerVisible) {
currentPanelClassName = "gisLayerPanel-isVisible";
currentPanel = <AddLayerPanel/>;
} else if (layerDetailsVisible) {
currentPanelClassName = "gisLayerPanel-isVisible";
currentPanel = (
<LayerPanel/>
componentWillUnmount() {
timeService.off('timeUpdate', this.props.setTimeFiltersToKbnGlobalTime);
timeService.off('refreshIntervalUpdate', this.setRefreshTimer);
this.clearRefreshTimer();
}
setRefreshTimer = () => {
this.clearRefreshTimer();
const { value, pause } = timeService.getRefreshInterval();
if (!pause && value > 0) {
this.refreshTimerId = setInterval(
() => {
this.props.triggerRefreshTimer();
},
value
);
}
this.props.setRefreshConfig({
isPaused: pause,
interval: value,
});
}
clearRefreshTimer = () => {
if (this.refreshTimerId) {
clearInterval(this.refreshTimerId);
}
}
render() {
const {
layerDetailsVisible,
addLayerVisible,
noFlyoutVisible
} = this.props;
let currentPanel;
let currentPanelClassName;
if (noFlyoutVisible) {
currentPanel = null;
} else if (addLayerVisible) {
currentPanelClassName = "gisLayerPanel-isVisible";
currentPanel = <AddLayerPanel/>;
} else if (layerDetailsVisible) {
currentPanelClassName = "gisLayerPanel-isVisible";
currentPanel = (
<LayerPanel/>
);
}
return (
<EuiFlexGroup gutterSize="none" responsive={false}>
<EuiFlexItem className="gisMapWrapper">
<MBMapContainer/>
<LayerControl/>
</EuiFlexItem>
<EuiFlexItem className={`gisLayerPanel ${currentPanelClassName}`} grow={false}>
{currentPanel}
</EuiFlexItem>
<Toasts/>
</EuiFlexGroup>
);
}
return (
<EuiFlexGroup gutterSize="none" responsive={false}>
<EuiFlexItem className="gisMapWrapper">
<MBMapContainer/>
<LayerControl/>
</EuiFlexItem>
<EuiFlexItem className={`gisLayerPanel ${currentPanelClassName}`} grow={false}>
{currentPanel}
</EuiFlexItem>
<Toasts/>
</EuiFlexGroup>
);
}

View file

@ -119,6 +119,10 @@ export const getMapColors = ({ map }) => {
export const getTimeFilters = ({ map }) => map.mapState.timeFilters ?
map.mapState.timeFilters : timefilter.getTime();
export const getRefreshConfig = ({ map }) => map.mapState.refreshConfig;
export const getRefreshTimerLastTriggeredAt = ({ map }) => map.mapState.refreshTimerLastTriggeredAt;
export const getMetadata = ({ config }) => config && config.meta;
export const getDataFilters = createSelector(
@ -126,12 +130,14 @@ export const getDataFilters = createSelector(
getMapBuffer,
getMapZoom,
getTimeFilters,
(mapExtent, mapBuffer, mapZoom, timeFilters) => {
getRefreshTimerLastTriggeredAt,
(mapExtent, mapBuffer, mapZoom, timeFilters, refreshTimerLastTriggeredAt) => {
return {
extent: mapExtent,
buffer: mapBuffer,
zoom: mapZoom,
timeFilters: timeFilters
timeFilters,
refreshTimerLastTriggeredAt,
};
}
);

View file

@ -100,10 +100,13 @@ export class HeatmapLayer extends ALayer {
const isSameTime = _.isEqual(dataMeta.timeFilters, dataFilters.timeFilters);
const updateDueToRefreshTimer = dataFilters.refreshTimerLastTriggeredAt
&& !_.isEqual(dataMeta.refreshTimerLastTriggeredAt, dataFilters.refreshTimerLastTriggeredAt);
const updateDueToExtent = this.updateDueToExtent(this._source, dataMeta, dataFilters);
if (isSamePrecision && isSameTime && !updateDueToExtent) {
if (isSamePrecision && isSameTime && !updateDueToExtent && !updateDueToRefreshTimer) {
return;
}

View file

@ -135,6 +135,10 @@ export class ESGeohashGridSource extends VectorSource {
return true;
}
isRefreshTimerAware() {
return true;
}
getFieldNames() {
return this.getMetricFields().map(({ propertyKey }) => {
return propertyKey;

View file

@ -157,10 +157,14 @@ export class ESJoinSource extends ASource {
}
isFilterByMapBounds() {
//todo
// TODO
return false;
}
isRefreshTimerAware() {
return true;
}
getJoinDescription(leftSourceName, leftFieldName) {
const metrics = this._getValidMetrics().map(metric => {
return metric.type !== 'count' ? `${metric.type}(${metric.field})` : 'count(*)';

View file

@ -72,6 +72,10 @@ export class ESSearchSource extends VectorSource {
return true;
}
isRefreshTimerAware() {
return true;
}
getFieldNames() {
return [
this._descriptor.geoField,

View file

@ -43,6 +43,10 @@ export class ASource {
return false;
}
isRefreshTimerAware() {
return false;
}
getFieldNames() {
return [];
}

View file

@ -166,10 +166,11 @@ export class VectorLayer extends ALayer {
async _canSkipSourceUpdate(source, sourceDataId, filters) {
const timeAware = await source.isTimeAware();
const refreshTimerAware = await source.isRefreshTimerAware();
const extentAware = source.isFilterByMapBounds();
const isFieldAware = source.isFieldAware();
if (!timeAware && !extentAware && !isFieldAware) {
if (!timeAware && !refreshTimerAware && !extentAware && !isFieldAware) {
const sourceDataRequest = this._findDataRequestForSource(sourceDataId);
if (sourceDataRequest && sourceDataRequest.hasDataOrRequestInProgress()) {
return true;
@ -192,13 +193,20 @@ export class VectorLayer extends ALayer {
updateDueToTime = !_.isEqual(meta.timeFilters, filters.timeFilters);
}
let updateDueToRefreshTimer = false;
if (refreshTimerAware && filters.refreshTimerLastTriggeredAt) {
updateDueToRefreshTimer = !_.isEqual(meta.refreshTimerLastTriggeredAt, filters.refreshTimerLastTriggeredAt);
}
let updateDueToFields = false;
if (isFieldAware) {
updateDueToFields = !_.isEqual(meta.fieldNames, filters.fieldNames);
}
return !updateDueToTime && !this.updateDueToExtent(source, meta, filters) && !updateDueToFields;
return !updateDueToTime
&& !updateDueToRefreshTimer
&& !this.updateDueToExtent(source, meta, filters)
&& !updateDueToFields;
}
async _syncJoin(join, { startLoading, stopLoading, onLoadError, dataFilters }) {

View file

@ -26,7 +26,9 @@ import {
SET_JOINS,
TOUCH_LAYER,
UPDATE_LAYER_ALPHA_VALUE,
UPDATE_SOURCE_PROP
UPDATE_SOURCE_PROP,
SET_REFRESH_CONFIG,
TRIGGER_REFRESH_TIMER,
} from "../actions/store_actions";
const getLayerIndex = (list, layerId) => list.findIndex(({ id }) => layerId === id);
@ -76,6 +78,8 @@ const INITIAL_STATE = {
},
extent: null,
timeFilters: null,
refreshConfig: null,
refreshTimerLastTriggeredAt: null,
},
selectedLayerId: null,
layerList: []
@ -119,6 +123,26 @@ export function map(state = INITIAL_STATE, action) {
case SET_TIME_FILTERS:
const { from, to } = action;
return { ...state, mapState: { ...state.mapState, timeFilters: { from, to } } };
case SET_REFRESH_CONFIG:
const { isPaused, interval } = action;
return {
...state,
mapState: {
...state.mapState,
refreshConfig: {
isPaused,
interval,
}
}
};
case TRIGGER_REFRESH_TIMER:
return {
...state,
mapState: {
...state.mapState,
refreshTimerLastTriggeredAt: (new Date()).toISOString(),
}
};
case SET_SELECTED_LAYER:
const match = state.layerList.find(layer => layer.id === action.selectedLayerId);
return { ...state, selectedLayerId: match ? action.selectedLayerId : null };

View file

@ -12,6 +12,14 @@ export default function ({ getPageObjects }) {
describe('layer geohashgrid aggregation source', () => {
async function getRequestTimestamp() {
await PageObjects.gis.openInspectorRequestsView();
const requestStats = await PageObjects.gis.getInspectorTableData();
const requestTimestamp = PageObjects.gis.getInspectorStatRowHit(requestStats, 'Request timestamp');
await PageObjects.gis.closeInspector();
return requestTimestamp;
}
describe('heatmap', () => {
before(async () => {
await PageObjects.gis.loadSavedMap('geohashgrid heatmap example');
@ -25,6 +33,14 @@ export default function ({ getPageObjects }) {
const EXPECTED_NUMBER_FEATURES = 6;
const HEATMAP_PROP_NAME = '__kbn_heatmap_weight__';
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp.length).to.be(24);
await PageObjects.gis.triggerSingleRefresh(1000);
const afterRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp).not.to.equal(afterRefreshTimerTimestamp);
});
it('should decorate feature properties with scaled doc_count property', async () => {
const mapboxStyle = await PageObjects.gis.getMapboxStyle();
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES);
@ -72,6 +88,14 @@ export default function ({ getPageObjects }) {
const EXPECTED_NUMBER_FEATURES = 6;
const MAX_OF_BYTES_PROP_NAME = 'max_of_bytes';
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp.length).to.be(24);
await PageObjects.gis.triggerSingleRefresh(1000);
const afterRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp).not.to.equal(afterRefreshTimerTimestamp);
});
it('should decorate feature properties with metrics properterties', async () => {
const mapboxStyle = await PageObjects.gis.getMapboxStyle();
expect(mapboxStyle.sources[LAYER_ID].data.features.length).to.equal(EXPECTED_NUMBER_FEATURES);

View file

@ -11,19 +11,34 @@ export default function ({ getPageObjects }) {
describe('elasticsearch document layer', () => {
before(async () => {
await PageObjects.gis.loadSavedMap('logstash events');
await PageObjects.gis.loadSavedMap('document example');
});
after(async () => {
await PageObjects.gis.closeInspector();
});
it('should re-fetch geohashgrid aggregation with refresh timer', async () => {
async function getRequestTimestamp() {
await PageObjects.gis.openInspectorRequestsView();
const requestStats = await PageObjects.gis.getInspectorTableData();
const requestTimestamp = PageObjects.gis.getInspectorStatRowHit(requestStats, 'Request timestamp');
await PageObjects.gis.closeInspector();
return requestTimestamp;
}
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp.length).to.be(24);
await PageObjects.gis.triggerSingleRefresh(1000);
const afterRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp).not.to.equal(afterRefreshTimerTimestamp);
});
describe('inspector', () => {
it('should register elasticsearch request in inspector', async () => {
await PageObjects.gis.openInspectorRequestsView();
const requestStats = await PageObjects.gis.getInspectorTableData();
const hits = PageObjects.gis.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('2048');
expect(hits).to.equal('6');
});
});
});

View file

@ -32,7 +32,6 @@ export default function ({ loadTestFile, getService }) {
loadTestFile(require.resolve('./saved_object_management'));
loadTestFile(require.resolve('./sample_data'));
loadTestFile(require.resolve('./load_saved_object'));
loadTestFile(require.resolve('./es_search_source'));
loadTestFile(require.resolve('./es_geohashgrid_source'));
loadTestFile(require.resolve('./joins'));

View file

@ -25,6 +25,22 @@ export default function ({ getPageObjects }) {
await PageObjects.gis.closeInspector();
});
it('should re-fetch join with refresh timer', async () => {
async function getRequestTimestamp() {
await PageObjects.gis.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await PageObjects.gis.getInspectorTableData();
const requestTimestamp = PageObjects.gis.getInspectorStatRowHit(requestStats, 'Request timestamp');
await PageObjects.gis.closeInspector();
return requestTimestamp;
}
const beforeRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp.length).to.be(24);
await PageObjects.gis.triggerSingleRefresh(1000);
const afterRefreshTimerTimestamp = await getRequestTimestamp();
expect(beforeRefreshTimerTimestamp).not.to.equal(afterRefreshTimerTimestamp);
});
it('should decorate feature properties with join property', async () => {
const mapboxStyle = await PageObjects.gis.getMapboxStyle();
expect(mapboxStyle.sources.n1t6f.data.features.length).to.equal(3);

View file

@ -1,34 +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;
* you may not use this file except in compliance with the Elastic License.
*/
import expect from 'expect.js';
export default function ({ getPageObjects }) {
const PageObjects = getPageObjects(['gis', 'header']);
describe('load gis-map saved objects', () => {
before(async () => {
await PageObjects.gis.loadSavedMap('logstash events');
});
after(async () => {
await PageObjects.gis.closeInspector();
});
describe('mapState', () => {
it('should update Kibana time to time defined in mapState', async () => {
const prettyPrint = await PageObjects.header.getPrettyDuration();
expect(prettyPrint).to.equal('September 20th 2015, 00:00:00.000 to September 23rd 2015, 00:00:00.000');
});
// TODO verify map center and zoom
// TODO verify map coordinate system
});
// TODO verify ui state like dark mode
});
}

View file

@ -8,7 +8,7 @@ import expect from 'expect.js';
export default function ({ getPageObjects }) {
const PageObjects = getPageObjects(['gis']);
const PageObjects = getPageObjects(['gis', 'header']);
describe('gis-map saved object management', () => {
@ -16,6 +16,34 @@ export default function ({ getPageObjects }) {
const MAP1_NAME = `${MAP_NAME_PREFIX}map1`;
const MAP2_NAME = `${MAP_NAME_PREFIX}map2`;
describe('read', () => {
before(async () => {
await PageObjects.gis.loadSavedMap('join example');
});
it('should update global Kibana time to value stored with map', async () => {
const kibanaTime = await PageObjects.header.getPrettyDuration();
expect(kibanaTime).to.equal('Last 17m');
});
it('should update global Kibana refresh config to value stored with map', async () => {
const kibanaRefreshConfig = await PageObjects.header.getRefreshConfig();
expect(kibanaRefreshConfig).to.equal('inactive 1 second');
});
it('should set map location to value stored with map', async () => {
const { lat, lon, zoom } = await PageObjects.gis.getView();
expect(lat).to.equal('-0.04647');
expect(lon).to.equal('77.33426');
expect(zoom).to.equal('3.02');
});
it('should load map layers stored with map', async () => {
const layerExists = await PageObjects.gis.doesLayerExist('geo_shapes*');
expect(layerExists).to.equal(true);
});
});
describe('create', () => {
it('should allow saving map', async () => {
await PageObjects.gis.openNewMap();

View file

@ -72,10 +72,10 @@
"source": {
"type" : "gis-map",
"gis-map" : {
"title" : "logstash events",
"title" : "document example",
"description" : "",
"mapStateJSON" : "{\"zoom\":4,\"center\":{\"lon\":-100.41,\"lat\":32.82},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-23T00:00:00.000Z\"}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"tmsy6\",\"label\":\"logstash events\",\"showAtAllZoomLevels\":false,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"type\":\"ES_SEARCH\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}}},\"previousStyle\":null},\"type\":\"VECTOR\"}]",
"mapStateJSON" : "{\"zoom\":4.1,\"center\":{\"lon\":-100.61091,\"lat\":33.23887},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"z52lq\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"e1a5e1a6-676c-4a89-8ea9-0d91d64b73c6\",\"type\":\"ES_SEARCH\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"limit\":2048,\"filterByMapBounds\":true,\"showTooltip\":true,\"tooltipProperties\":[]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#e6194b\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#FFFFFF\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"alphaValue\":1},\"previousStyle\":null},\"type\":\"VECTOR\"}]",
"uiStateJSON" : "{\"isDarkMode\":false}",
"bounds" : {
"type" : "envelope",
@ -106,7 +106,7 @@
"gis-map" : {
"title" : "join example",
"description" : "",
"mapStateJSON" : "{\"zoom\":3.02,\"center\":{\"lon\":77.33426,\"lat\":-0.04647},\"timeFilters\":{\"from\":\"now-15m\",\"to\":\"now\",\"mode\":\"quick\"}}",
"mapStateJSON" : "{\"zoom\":3.02,\"center\":{\"lon\":77.33426,\"lat\":-0.04647},\"timeFilters\":{\"from\":\"now-17m\",\"to\":\"now\",\"mode\":\"quick\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"label\":\"EMS base layer (road_map)\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"n1t6f\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"id\":\"62eca1fc-fe42-11e8-8eb2-f2801f1b9fd1\",\"type\":\"ES_SEARCH\",\"indexPatternId\":\"561253e0-f731-11e8-8487-11b9dd924f96\",\"geoField\":\"geometry\",\"limit\":2048,\"filterByMapBounds\":false,\"showTooltip\":true,\"tooltipProperties\":[]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max(prop1) group by meta_for_geo_shapes*.shape_name\",\"name\":\"__kbnjoin__max_of_prop1_groupby_meta_for_geo_shapes*.shape_name\",\"origin\":\"join\"},\"color\":\"Blues\"}},\"iconSize\":{\"type\":\"STATIC\",\"options\":{\"size\":10}},\"alphaValue\":1},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\",\"joins\":[{\"leftField\":\"name\",\"right\":{\"id\":\"855ccb86-fe42-11e8-8eb2-f2801f1b9fd1\",\"indexPatternId\":\"e20b2a30-f735-11e8-8ce0-9723965e01e3\",\"indexPatternTitle\":\"meta_for_geo_shapes*\",\"term\":\"shape_name\",\"metrics\":[{\"type\":\"max\",\"field\":\"prop1\"}]}}]}]",
"uiStateJSON" : "{\"isDarkMode\":false}",
"bounds" : {
@ -137,7 +137,7 @@
"type" : "gis-map",
"gis-map" : {
"title" : "geohashgrid heatmap example",
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"}}",
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"3xlvm\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"type\":\"ES_GEOHASH_GRID\",\"id\":\"427aa49d-a552-4e7d-a629-67c47db27128\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"heatmap\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"HEATMAP\",\"refinement\":\"coarse\",\"properties\":{\"alphaValue\":1},\"previousStyle\":null},\"type\":\"HEATMAP\"}]",
"uiStateJSON" : "{\"isDarkMode\":false}",
"bounds" : {
@ -169,7 +169,7 @@
"gis-map" : {
"title" : "geohashgrid vector grid example",
"description" : "",
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"}}",
"mapStateJSON" : "{\"zoom\":3.59,\"center\":{\"lon\":-98.05765,\"lat\":38.32288},\"timeFilters\":{\"from\":\"2015-09-20T00:00:00.000Z\",\"to\":\"2015-09-20T01:00:00.000Z\"},\"refreshConfig\":{\"isPaused\":true,\"interval\":1000}}",
"layerListJSON" : "[{\"id\":\"0hmz5\",\"sourceDescriptor\":{\"type\":\"EMS_TMS\",\"id\":\"road_map\"},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"TILE\",\"properties\":{\"alphaValue\":1}},\"type\":\"TILE\",\"minZoom\":0,\"maxZoom\":24},{\"id\":\"g1xkv\",\"label\":null,\"minZoom\":0,\"maxZoom\":24,\"sourceDescriptor\":{\"type\":\"ES_GEOHASH_GRID\",\"id\":\"9305f6ea-4518-4c06-95b9-33321aa38d6a\",\"indexPatternId\":\"c698b940-e149-11e8-a35a-370a8516603a\",\"geoField\":\"geo.coordinates\",\"requestType\":\"grid\",\"metrics\":[{\"type\":\"count\"},{\"type\":\"max\",\"field\":\"bytes\"}]},\"visible\":true,\"temporary\":false,\"style\":{\"type\":\"VECTOR\",\"properties\":{\"fillColor\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"max of bytes\",\"name\":\"max_of_bytes\",\"origin\":\"source\"},\"color\":\"Blues\"}},\"lineColor\":{\"type\":\"STATIC\",\"options\":{\"color\":\"#cccccc\"}},\"lineWidth\":{\"type\":\"STATIC\",\"options\":{\"size\":1}},\"iconSize\":{\"type\":\"DYNAMIC\",\"options\":{\"field\":{\"label\":\"Count\",\"name\":\"doc_count\",\"origin\":\"source\"},\"minSize\":4,\"maxSize\":32}},\"alphaValue\":1},\"temporary\":true,\"previousStyle\":null},\"type\":\"VECTOR\"}]",
"uiStateJSON" : "{\"isDarkMode\":false}",
"bounds" : {

View file

@ -107,26 +107,23 @@ export function GisPageProvider({ getService, getPageObjects }) {
async setView(lat, lon, zoom) {
log.debug(`Set view lat: ${lat}, lon: ${lon}, zoom: ${zoom}`);
await testSubjects.click('toggleSetViewVisibilityButton');
const latInput = await testSubjects.find('latitudeInput');
await latInput.clearValue();
await latInput.click();
await latInput.type(lat);
const lonInput = await testSubjects.find('longitudeInput');
await lonInput.clearValue();
await lonInput.click();
await lonInput.type(lon);
const zoomInput = await testSubjects.find('zoomInput');
await zoomInput.clearValue();
await zoomInput.click();
await zoomInput.type(zoom);
await testSubjects.setValue('latitudeInput', lat);
await testSubjects.setValue('longitudeInput', lon);
await testSubjects.setValue('zoomInput', zoom);
await testSubjects.click('submitViewButton');
await PageObjects.header.waitUntilLoadingHasFinished();
}
async getView() {
log.debug('Get view');
await testSubjects.click('toggleSetViewVisibilityButton');
const lat = await testSubjects.getAttribute('latitudeInput', 'value');
const lon = await testSubjects.getAttribute('longitudeInput', 'value');
const zoom = await testSubjects.getAttribute('zoomInput', 'value');
await testSubjects.click('toggleSetViewVisibilityButton');
return { lat, lon, zoom };
}
async openLayerPanel(layerName) {
log.debug(`Open layer panel, layer: ${layerName}`);
await testSubjects.click(`mapOpenLayerButton${layerName}`);
@ -245,6 +242,14 @@ export function GisPageProvider({ getService, getPageObjects }) {
return statsRow[STATS_ROW_VALUE_INDEX];
}
async triggerSingleRefresh(refreshInterval) {
log.debug(`triggerSingleRefresh, refreshInterval: ${refreshInterval}`);
await PageObjects.header.resumeAutoRefresh();
log.debug('waiting to give time for refresh timer to fire');
await PageObjects.common.sleep(refreshInterval + (refreshInterval / 2));
await PageObjects.header.pauseAutoRefresh();
await PageObjects.header.waitUntilLoadingHasFinished();
}
}
return new GisPage();
}