[Maps] ignore global query layer setting (#35542)

* [Maps] ignore global query layer setting

* do not include index pattern id in query bar indexPatterns when applyGlobalQuery is disabled

* update how embeddable factory extracts indexPatterns so only queryable index patterns are included in list

* support applyGlobalQuery for heatmap and getBounds

* add functional tests to join layer

* show filter section when layer has join

* move checkbox to layer settings panel

* text review

* set checkbox to false when disabled

* do not trigger refetch when global query and global filter changes and applyGlobalQuery is disabled

* rename applyGlobalQuery to getApplyGlobalQuery

* throw error in map embeddable factory if layerListJSON can not be parsed

* remove extra space

* update zoom range slider label and remove nested EuiFormRow
This commit is contained in:
Nathan Reese 2019-04-26 19:20:11 -06:00 committed by GitHub
parent 27de17abbb
commit 8c687ed85a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 458 additions and 79 deletions

View file

@ -156,6 +156,15 @@ export function addLayer(layerDescriptor) {
};
}
// Do not use when rendering a map. Method exists to enable selectors for getLayerList when
// rendering is not needed.
export function addLayerWithoutDataSync(layerDescriptor) {
return {
type: ADD_LAYER,
layer: layerDescriptor,
};
}
function setLayerDataLoadErrorStatus(layerId, errorMessage) {
return dispatch => {
dispatch({
@ -511,6 +520,19 @@ export function setLayerQuery(id, query) {
};
}
export function setLayerApplyGlobalQuery(id, applyGlobalQuery) {
return (dispatch) => {
dispatch({
type: UPDATE_LAYER_PROP,
id,
propName: 'applyGlobalQuery',
newValue: applyGlobalQuery,
});
dispatch(syncDataForLayer(id));
};
}
export function removeSelectedLayer() {
return (dispatch, getState) => {
const state = getState();

View file

@ -62,22 +62,23 @@ describe('Saved object does not have layer list', () => {
};
const layers = getInitialLayers(null);
expect(layers).toEqual([{
'alpha': 1,
'__dataRequests': [],
'id': layers[0].id,
'label': null,
'maxZoom': 24,
'minZoom': 0,
'sourceDescriptor': {
'type': 'EMS_TMS',
'id': 'road_map',
alpha: 1,
__dataRequests: [],
id: layers[0].id,
applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,
sourceDescriptor: {
type: 'EMS_TMS',
id: 'road_map',
},
'style': {
'properties': {},
'type': 'TILE',
style: {
properties: {},
type: 'TILE',
},
'type': 'TILE',
'visible': true,
type: 'TILE',
visible: true,
}]);
});
@ -91,9 +92,10 @@ describe('Saved object does not have layer list', () => {
const layers = getInitialLayers(null);
expect(layers).toEqual([{
'alpha': 1,
alpha: 1,
__dataRequests: [],
id: layers[0].id,
applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,
@ -117,9 +119,10 @@ describe('Saved object does not have layer list', () => {
const layers = getInitialLayers(null);
expect(layers).toEqual([{
'alpha': 1,
alpha: 1,
__dataRequests: [],
id: layers[0].id,
applyGlobalQuery: true,
label: null,
maxZoom: 24,
minZoom: 0,

View file

@ -35,7 +35,7 @@ import {
setReadOnly,
setIsLayerTOCOpen
} from '../store/ui';
import { getUniqueIndexPatternIds } from '../selectors/map_selectors';
import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
import { getInspectorAdapters } from '../store/non_serializable_instances';
import { Inspector } from 'ui/inspector';
import { DocTitleProvider } from 'ui/doc_title';
@ -197,7 +197,7 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl, localStorage
});
}
const nextIndexPatternIds = getUniqueIndexPatternIds(store.getState());
const nextIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
if (nextIndexPatternIds !== prevIndexPatternIds) {
prevIndexPatternIds = nextIndexPatternIds;
updateIndexPatterns(nextIndexPatternIds);

View file

@ -128,6 +128,7 @@ export class FilterEditor extends Component {
</EuiTextColor>
</p>
</EuiText>
);
}
@ -174,8 +175,13 @@ export class FilterEditor extends Component {
/>
</h5>
</EuiTitle>
<EuiSpacer size="m"/>
{this._renderQuery()}
{this._renderQueryPopover()}
</Fragment>
);
}

View file

@ -14,7 +14,7 @@ exports[`Should render errors when layer has errors 1`] = `
</p>
</EuiCallOut>
<EuiSpacer
margin="m"
size="m"
/>
</Fragment>
`;

View file

@ -31,7 +31,7 @@ export function LayerErrors({ layer }) {
{layer.getErrors()}
</p>
</EuiCallOut>
<EuiSpacer margin="m"/>
<EuiSpacer size="m"/>
</Fragment>
);
}

View file

@ -12,12 +12,14 @@ import {
updateLayerMaxZoom,
updateLayerMinZoom,
updateLayerAlpha,
setLayerApplyGlobalQuery,
} from '../../../actions/store_actions';
function mapStateToProps(state = {}) {
const selectedLayer = getSelectedLayer(state);
return {
alpha: selectedLayer.getAlpha(),
applyGlobalQuery: selectedLayer.getApplyGlobalQuery(),
label: selectedLayer.getLabel(),
layerId: selectedLayer.getId(),
maxZoom: selectedLayer.getMaxZoom(),
@ -32,6 +34,9 @@ function mapDispatchToProps(dispatch) {
updateMinZoom: (id, minZoom) => dispatch(updateLayerMinZoom(id, minZoom)),
updateMaxZoom: (id, maxZoom) => dispatch(updateLayerMaxZoom(id, maxZoom)),
updateAlpha: (id, alpha) => dispatch(updateLayerAlpha(id, alpha)),
setLayerApplyGlobalQuery: (layerId, applyGlobalQuery) => {
dispatch(setLayerApplyGlobalQuery(layerId, applyGlobalQuery));
}
};
}

View file

@ -14,6 +14,8 @@ import {
EuiFormRow,
EuiFieldText,
EuiSpacer,
EuiSwitch,
EuiToolTip,
} from '@elastic/eui';
import { ValidatedRange } from '../../../shared/components/validated_range';
@ -40,35 +42,26 @@ export function LayerSettings(props) {
props.updateAlpha(props.layerId, alpha);
};
const onApplyGlobalQueryChange = event => {
props.setLayerApplyGlobalQuery(props.layerId, event.target.checked);
};
const renderZoomSliders = () => {
return (
<EuiFormRow
helpText={
i18n.translate('xpack.maps.layerPanel.settingsPanel.zoomFeedbackHelptext', {
defaultMessage: 'Display layer when map is in zoom range.'
})
}
label={i18n.translate('xpack.maps.layerPanel.settingsPanel.visibleZoomLabel', {
defaultMessage: 'Zoom range for layer visibility'
})}
>
<EuiFlexGroup>
<EuiFlexItem>
<EuiFormRow
label={i18n.translate('xpack.maps.layerPanel.settingsPanel.visibleZoomLabel', {
defaultMessage: 'Visible zoom range'
})}
>
<ValidatedDualRange
min={MIN_ZOOM}
max={MAX_ZOOM}
value={[props.minZoom, props.maxZoom]}
showInput
showRange
onChange={onZoomChange}
allowEmptyRange={false}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
<ValidatedDualRange
min={MIN_ZOOM}
max={MAX_ZOOM}
value={[props.minZoom, props.maxZoom]}
showInput
showRange
onChange={onZoomChange}
allowEmptyRange={false}
/>
</EuiFormRow>
);
};
@ -115,6 +108,43 @@ export function LayerSettings(props) {
);
};
const renderApplyGlobalQueryCheckbox = () => {
const layerSupportsGlobalQuery = props.layer.getIndexPatternIds().length;
const applyGlobalQueryCheckbox = (
<EuiFormRow>
<EuiSwitch
label={
i18n.translate('xpack.maps.layerPanel.applyGlobalQueryCheckboxLabel', {
defaultMessage: `Apply global filter to layer`
})
}
checked={layerSupportsGlobalQuery ? props.applyGlobalQuery : false}
onChange={onApplyGlobalQueryChange}
disabled={!layerSupportsGlobalQuery}
data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox"
/>
</EuiFormRow>
);
if (layerSupportsGlobalQuery) {
return applyGlobalQueryCheckbox;
}
return (
<EuiToolTip
position="top"
content={
i18n.translate('xpack.maps.layerPanel.applyGlobalQueryCheckbox.disableTooltip', {
defaultMessage: `Layer does not support filtering.`
})
}
>
{applyGlobalQueryCheckbox}
</EuiToolTip>
);
};
return (
<Fragment>
<EuiPanel>
@ -131,13 +161,15 @@ export function LayerSettings(props) {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer margin="m"/>
<EuiSpacer size="m"/>
{renderLabel()}
{renderZoomSliders()}
{renderAlphaSlider()}
{renderApplyGlobalQueryCheckbox()}
</EuiPanel>
<EuiSpacer size="s" />

View file

@ -23,7 +23,7 @@ exports[`Should render source settings editor 1`] = `
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer
margin="m"
size="m"
/>
<div>
mockSourceEditor

View file

@ -44,7 +44,7 @@ export function SourceSettings({ layer, updateSourceProp }) {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer margin="m"/>
<EuiSpacer size="m"/>
{sourceSettingsEditor}
</EuiPanel>

View file

@ -40,7 +40,7 @@ export function StyleTabs({ layer, updateStyle }) {
<EuiPanel key={index}>
<EuiTitle size="xs"><h5>{Style.getDisplayName()}</h5></EuiTitle>
{description}
<EuiSpacer margin="m"/>
<EuiSpacer size="m"/>
{styleEditor}
</EuiPanel>
);

View file

@ -11,6 +11,9 @@ import { MapEmbeddable } from './map_embeddable';
import { indexPatternService } from '../kibana_services';
import { i18n } from '@kbn/i18n';
import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
import { createMapStore } from '../store/store';
import { addLayerWithoutDataSync } from '../actions/store_actions';
import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
export class MapEmbeddableFactory extends EmbeddableFactory {
@ -28,8 +31,21 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
this._savedObjectLoader = gisMapSavedObjectLoader;
}
async _getIndexPatterns(indexPatternIds = []) {
const promises = indexPatternIds.map(async (indexPatternId) => {
async _getIndexPatterns(layerListJSON) {
// Need to extract layerList from store to get queryable index pattern ids
const store = createMapStore();
try {
JSON.parse(layerListJSON).forEach(layerDescriptor => {
store.dispatch(addLayerWithoutDataSync(layerDescriptor));
});
} catch (error) {
throw new Error(i18n.translate('xpack.maps.mapEmbeddableFactory', {
defaultMessage: 'Unable to load map, malformed saved object',
}));
}
const queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
const promises = queryableIndexPatternIds.map(async (indexPatternId) => {
try {
return await indexPatternService.get(indexPatternId);
} catch (error) {
@ -44,7 +60,8 @@ export class MapEmbeddableFactory extends EmbeddableFactory {
async create(panelMetadata, onEmbeddableStateChanged) {
const savedMap = await this._savedObjectLoader.get(panelMetadata.id);
const indexPatterns = await this._getIndexPatterns(savedMap.indexPatternIds);
const indexPatterns = await this._getIndexPatterns(savedMap.layerListJSON);
return new MapEmbeddable({
onEmbeddableStateChanged,

View file

@ -170,6 +170,7 @@ export const getSelectedLayerJoinDescriptors = createSelector(
});
});
// Get list of unique index patterns used by all layers
export const getUniqueIndexPatternIds = createSelector(
getLayerList,
(layerList) => {
@ -181,6 +182,18 @@ export const getUniqueIndexPatternIds = createSelector(
}
);
// Get list of unique index patterns, excluding index patterns from layers that disable applyGlobalQuery
export const getQueryableUniqueIndexPatternIds = createSelector(
getLayerList,
(layerList) => {
const indexPatternIds = [];
layerList.forEach(layer => {
indexPatternIds.push(...layer.getQueryableIndexPatternIds());
});
return _.uniq(indexPatternIds);
}
);
export const hasDirtyState = createSelector(getLayerListRaw, (layerListRaw) => {
return layerListRaw.some(layerDescriptor => {
const currentState = copyPersistentState(layerDescriptor);

View file

@ -10,6 +10,7 @@ import { AbstractLayer } from './layer';
import { EuiIcon } from '@elastic/eui';
import { HeatmapStyle } from './styles/heatmap_style';
import { SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
import { isRefreshOnlyQuery } from './util/is_refresh_only_query';
const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__';//unique name to store scaled value for weighting
@ -45,7 +46,6 @@ export class HeatmapLayer extends AbstractLayer {
return metricfields[0].propertyKey;
}
_getMbLayerId() {
return this.getId() + '_heatmap';
}
@ -134,13 +134,19 @@ export class HeatmapLayer extends AbstractLayer {
const updateDueToExtent = this.updateDueToExtent(this._source, meta, searchFilters);
const updateDueToQuery = searchFilters.query
&& !_.isEqual(meta.query, searchFilters.query);
let updateDueToQuery = false;
let updateDueToFilters = false;
if (searchFilters.applyGlobalQuery) {
updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
} else {
// Global filters and query are not applied to layer search request so no re-fetch required.
// Exception is "Refresh" query.
updateDueToQuery = isRefreshOnlyQuery(meta.query, searchFilters.query);
}
const updateDueToLayerQuery = searchFilters.layerQuery
&& !_.isEqual(meta.layerQuery, searchFilters.layerQuery);
const updateDueToFilters = searchFilters.filters
&& !_.isEqual(meta.filters, searchFilters.filters);
const updateDueToApplyGlobalQuery = meta.applyGlobalQuery !== searchFilters.applyGlobalQuery;
const updateDueToMetricChange = !_.isEqual(meta.metric, searchFilters.metric);
@ -150,6 +156,7 @@ export class HeatmapLayer extends AbstractLayer {
&& !updateDueToRefreshTimer
&& !updateDueToQuery
&& !updateDueToLayerQuery
&& !updateDueToApplyGlobalQuery
&& !updateDueToFilters
&& !updateDueToMetricChange
) {
@ -163,6 +170,7 @@ export class HeatmapLayer extends AbstractLayer {
return {
...dataFilters,
layerQuery: this.getQuery(),
applyGlobalQuery: this.getApplyGlobalQuery(),
geogridPrecision: this._source.getGeoGridPrecision(dataFilters.zoom),
metric: this._getPropKeyOfSelectedMetric()
};

View file

@ -42,7 +42,9 @@ export class AbstractLayer {
layerDescriptor.maxZoom = _.get(options, 'maxZoom', 24);
layerDescriptor.alpha = _.get(options, 'alpha', 0.75);
layerDescriptor.visible = _.get(options, 'visible', true);
layerDescriptor.applyGlobalQuery = _.get(options, 'applyGlobalQuery', true);
layerDescriptor.style = _.get(options, 'style', {});
return layerDescriptor;
}
@ -144,6 +146,10 @@ export class AbstractLayer {
return this._descriptor.query;
}
getApplyGlobalQuery() {
return this._descriptor.applyGlobalQuery;
}
getZoomConfig() {
return {
minZoom: this._descriptor.minZoom,
@ -262,6 +268,14 @@ export class AbstractLayer {
return [];
}
getQueryableIndexPatternIds() {
if (this.getApplyGlobalQuery()) {
return this.getIndexPatternIds();
}
return [];
}
async getOrdinalFields() {
return [];
}

View file

@ -142,7 +142,9 @@ export class AbstractESSource extends AbstractVectorSource {
async _makeSearchSource(searchFilters, limit) {
const indexPattern = await this._getIndexPattern();
const isTimeAware = await this.isTimeAware();
const allFilters = [...searchFilters.filters];
const applyGlobalQuery = _.get(searchFilters, 'applyGlobalQuery', true);
const globalFilters = applyGlobalQuery ? searchFilters.filters : [];
const allFilters = [...globalFilters];
if (this.isFilterByMapBounds() && searchFilters.buffer) {//buffer can be empty
const geoField = await this._getGeoField();
allFilters.push(createExtentFilter(searchFilters.buffer, geoField.name, geoField.type));
@ -155,7 +157,9 @@ export class AbstractESSource extends AbstractVectorSource {
searchSource.setField('index', indexPattern);
searchSource.setField('size', limit);
searchSource.setField('filter', allFilters);
searchSource.setField('query', searchFilters.query);
if (applyGlobalQuery) {
searchSource.setField('query', searchFilters.query);
}
if (searchFilters.layerQuery) {
const layerSearchSource = new SearchSource();
@ -167,9 +171,9 @@ export class AbstractESSource extends AbstractVectorSource {
return searchSource;
}
async getBoundsForFilters({ layerQuery, query, timeFilters, filters }) {
async getBoundsForFilters({ layerQuery, query, timeFilters, filters, applyGlobalQuery }) {
const searchSource = await this._makeSearchSource({ layerQuery, query, timeFilters, filters }, 0);
const searchSource = await this._makeSearchSource({ layerQuery, query, timeFilters, filters, applyGlobalQuery }, 0);
const geoField = await this._getGeoField();
const indexPattern = await this._getIndexPattern();

View file

@ -0,0 +1,13 @@
/*
* 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.
*/
// Refresh only query is query where timestamps are different but query is the same.
// Triggered by clicking "Refresh" button in QueryBar
export function isRefreshOnlyQuery(prevQuery, newQuery) {
return prevQuery.queryLastTriggeredAt !== newQuery.queryLastTriggeredAt
&& prevQuery.language === newQuery.language
&& prevQuery.query === newQuery.query;
}

View file

@ -11,6 +11,7 @@ import { LeftInnerJoin } from './joins/left_inner_join';
import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_ID_ORIGIN } from '../../../common/constants';
import _ from 'lodash';
import { JoinTooltipProperty } from './tooltips/join_tooltip_property';
import { isRefreshOnlyQuery } from './util/is_refresh_only_query';
const EMPTY_FEATURE_COLLECTION = {
type: 'FeatureCollection',
@ -207,10 +208,18 @@ export class VectorLayer extends AbstractLayer {
let updateDueToQuery = false;
let updateDueToFilters = false;
let updateDueToLayerQuery = false;
let updateDueToApplyGlobalQuery = false;
if (isQueryAware) {
updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
updateDueToApplyGlobalQuery = meta.applyGlobalQuery !== searchFilters.applyGlobalQuery;
updateDueToLayerQuery = !_.isEqual(meta.layerQuery, searchFilters.layerQuery);
if (searchFilters.applyGlobalQuery) {
updateDueToQuery = !_.isEqual(meta.query, searchFilters.query);
updateDueToFilters = !_.isEqual(meta.filters, searchFilters.filters);
} else {
// Global filters and query are not applied to layer search request so no re-fetch required.
// Exception is "Refresh" query.
updateDueToQuery = isRefreshOnlyQuery(meta.query, searchFilters.query);
}
}
let updateDueToPrecisionChange = false;
@ -227,6 +236,7 @@ export class VectorLayer extends AbstractLayer {
&& !updateDueToQuery
&& !updateDueToFilters
&& !updateDueToLayerQuery
&& !updateDueToApplyGlobalQuery
&& !updateDueToPrecisionChange;
}
@ -236,22 +246,27 @@ export class VectorLayer extends AbstractLayer {
const sourceDataId = join.getSourceId();
const requestToken = Symbol(`layer-join-refresh:${ this.getId()} - ${sourceDataId}`);
const searchFilters = {
...dataFilters,
applyGlobalQuery: this.getApplyGlobalQuery(),
};
const canSkip = await this._canSkipSourceUpdate(joinSource, sourceDataId, searchFilters);
if (canSkip) {
const sourceDataRequest = this._findDataRequestForSource(sourceDataId);
const propertiesMap = sourceDataRequest ? sourceDataRequest.getData() : null;
return {
dataHasChanged: false,
join: join,
propertiesMap: propertiesMap
};
}
try {
const canSkip = await this._canSkipSourceUpdate(joinSource, sourceDataId, dataFilters);
if (canSkip) {
const sourceDataRequest = this._findDataRequestForSource(sourceDataId);
const propertiesMap = sourceDataRequest ? sourceDataRequest.getData() : null;
return {
dataHasChanged: false,
join: join,
propertiesMap: propertiesMap
};
}
startLoading(sourceDataId, requestToken, dataFilters);
startLoading(sourceDataId, requestToken, searchFilters);
const leftSourceName = await this.getSourceName();
const {
propertiesMap
} = await joinSource.getPropertiesMap(dataFilters, leftSourceName, join.getLeftFieldName());
} = await joinSource.getPropertiesMap(searchFilters, leftSourceName, join.getLeftFieldName());
stopLoading(sourceDataId, requestToken, propertiesMap);
return {
dataHasChanged: true,
@ -289,7 +304,8 @@ export class VectorLayer extends AbstractLayer {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: this._source.getGeoGridPrecision(dataFilters.zoom),
layerQuery: this.getQuery()
layerQuery: this.getQuery(),
applyGlobalQuery: this.getApplyGlobalQuery(),
};
}

View file

@ -0,0 +1,185 @@
/*
* 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.
*/
jest.mock('./joins/left_inner_join', () => ({
LeftInnerJoin: Object
}));
jest.mock('./tooltips/join_tooltip_property', () => ({
JoinTooltipProperty: Object
}));
import { VectorLayer } from './vector_layer';
describe('_canSkipSourceUpdate', () => {
const SOURCE_DATA_REQUEST_ID = 'foo';
describe('isQueryAware', () => {
const queryAwareSourceMock = {
isTimeAware: () => { return false; },
isRefreshTimerAware: () => { return false; },
isFilterByMapBounds: () => { return false; },
isFieldAware: () => { return false; },
isQueryAware: () => { return true; },
isGeoGridPrecisionAware: () => { return false; },
};
const prevFilters = [];
const prevQuery = {
language: 'kuery',
query: 'machine.os.keyword : "win 7"',
queryLastTriggeredAt: '2019-04-25T20:53:22.331Z'
};
describe('applyGlobalQuery is false', () => {
const prevApplyGlobalQuery = false;
const vectorLayer = new VectorLayer({
layerDescriptor: {
__dataRequests: [
{
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
}
}
]
}
});
it('can skip update when filter changes', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: [prevQuery],
query: prevQuery,
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(true);
});
it('can skip update when query changes', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
query: 'a new query string',
}
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(true);
});
it('can not skip update when query is refreshed', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
queryLastTriggeredAt: 'sometime layer when Refresh button is clicked'
}
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
it('can not skip update when applyGlobalQuery changes', async () => {
const searchFilters = {
applyGlobalQuery: !prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
});
describe('applyGlobalQuery is true', () => {
const prevApplyGlobalQuery = true;
const vectorLayer = new VectorLayer({
layerDescriptor: {
__dataRequests: [
{
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
}
}
]
}
});
it('can not skip update when filter changes', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: [prevQuery],
query: prevQuery,
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
it('can not skip update when query changes', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
query: 'a new query string',
}
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
it('can not skip update when query is refreshed', async () => {
const searchFilters = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
queryLastTriggeredAt: 'sometime layer when Refresh button is clicked'
}
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
it('can not skip update when applyGlobalQuery changes', async () => {
const searchFilters = {
applyGlobalQuery: !prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery
};
const canSkipUpdate = await vectorLayer._canSkipSourceUpdate(queryAwareSourceMock, SOURCE_DATA_REQUEST_ID, searchFilters);
expect(canSkipUpdate).toBe(false);
});
});
});
});

View file

@ -82,12 +82,30 @@ export default function ({ getPageObjects, getService }) {
});
describe('inspector', () => {
describe('query bar', () => {
before(async () => {
await PageObjects.maps.setAndSubmitQuery('prop1 < 10 or _index : "geo_shapes*"');
});
afterEach(async () => {
await inspector.close();
});
it('should contain terms aggregation elasticsearch request', async () => {
it('should apply query to join request', async () => {
await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
expect(totalHits).to.equal('3');
const hits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits');
expect(hits).to.equal('0'); // aggregation requests do not return any documents
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Index pattern');
expect(indexPatternName).to.equal('meta_for_geo_shapes*');
});
it('should not apply query to join request when apply global query is disabled', async () => {
await PageObjects.maps.openLayerPanel('geo_shapes*');
await PageObjects.maps.disableApplyGlobalQuery();
await PageObjects.maps.openInspectorRequest('meta_for_geo_shapes*.shape_name');
const requestStats = await inspector.getTableData();
const totalHits = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Hits (total)');
@ -97,6 +115,13 @@ export default function ({ getPageObjects, getService }) {
const indexPatternName = PageObjects.maps.getInspectorStatRowHit(requestStats, 'Index pattern');
expect(indexPatternName).to.equal('meta_for_geo_shapes*');
});
});
describe('inspector', () => {
afterEach(async () => {
await inspector.close();
});
it('should not contain any elasticsearch request after layer is deleted', async () => {
await PageObjects.maps.removeLayer('geo_shapes*');

View file

@ -259,6 +259,22 @@ export function GisPageProvider({ getService, getPageObjects }) {
await testSubjects.click(`mapOpenLayerButton${layerName}`);
}
async disableApplyGlobalQuery() {
const element = await testSubjects.find('mapLayerPanelApplyGlobalQueryCheckbox');
const isSelected = await element.isSelected();
if(isSelected) {
await retry.try(async () => {
log.debug(`disabling applyGlobalQuery`);
await testSubjects.click('mapLayerPanelApplyGlobalQueryCheckbox');
const isStillSelected = await element.isSelected();
if (isStillSelected) {
throw new Error('applyGlobalQuery not disabled');
}
});
await this.waitForLayersToLoad();
}
}
async doesLayerExist(layerName) {
layerName = layerName.replace(' ', '_');
log.debug(`Open layer panel, layer: ${layerName}`);