[Maps] Add force-refresh toggle (#104691)

Adds the ability for users to control whether a layer should refresh on auto-update or when the refresh-button is clicked. This is required to display static layers from geo-data indexed in Elasticsearch.
This commit is contained in:
Thomas Neirynck 2021-09-09 13:34:40 -04:00 committed by GitHub
parent c51c92cd7a
commit c006c82db9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 536 additions and 422 deletions

View file

@ -11,6 +11,7 @@ export const mockLayerList = [
{
leftField: 'iso2',
right: {
applyForceRefresh: true,
applyGlobalQuery: true,
applyGlobalTime: true,
type: 'ES_TERM_SOURCE',
@ -86,6 +87,7 @@ export const mockLayerList = [
{
leftField: 'region_iso_code',
right: {
applyForceRefresh: true,
applyGlobalQuery: true,
applyGlobalTime: true,
type: 'ES_TERM_SOURCE',

View file

@ -45,6 +45,7 @@ const ES_TERM_SOURCE_COUNTRY: ESTermSourceDescriptor = {
indexPatternId: APM_STATIC_INDEX_PATTERN_ID,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
};
const ES_TERM_SOURCE_REGION: ESTermSourceDescriptor = {
@ -60,6 +61,7 @@ const ES_TERM_SOURCE_REGION: ESTermSourceDescriptor = {
indexPatternId: APM_STATIC_INDEX_PATTERN_ID,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
};
const getWhereQuery = (serviceName: string) => {

View file

@ -7,10 +7,10 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import { Query } from 'src/plugins/data/public';
import type { Query } from 'src/plugins/data/common';
import { SortDirection } from 'src/plugins/data/common/search';
import { RENDER_AS, SCALING_TYPES } from '../constants';
import { MapExtent, MapQuery } from './map_descriptor';
import { MapExtent } from './map_descriptor';
import { Filter, TimeRange } from '../../../../../src/plugins/data/common';
import { ESTermSourceDescriptor } from './source_descriptor_types';
@ -20,12 +20,11 @@ export type Timeslice = {
};
// Global map state passed to every layer.
export type MapFilters = {
export type DataFilters = {
buffer?: MapExtent; // extent with additional buffer
extent?: MapExtent; // map viewport
filters: Filter[];
query?: MapQuery;
refreshTimerLastTriggeredAt?: string;
query?: Query;
searchSessionId?: string;
timeFilters: TimeRange;
timeslice?: Timeslice;
@ -60,24 +59,24 @@ export type VectorSourceSyncMeta =
| ESTermSourceSyncMeta
| null;
export type VectorSourceRequestMeta = MapFilters & {
export type VectorSourceRequestMeta = DataFilters & {
applyGlobalQuery: boolean;
applyGlobalTime: boolean;
applyForceRefresh: boolean;
fieldNames: string[];
geogridPrecision?: number;
timesiceMaskField?: string;
sourceQuery?: MapQuery;
sourceMeta: VectorSourceSyncMeta;
};
export type VectorJoinSourceRequestMeta = Omit<VectorSourceRequestMeta, 'geogridPrecision'> & {
timesliceMaskField?: string;
sourceQuery?: Query;
sourceMeta: VectorSourceSyncMeta;
isForceRefresh: boolean;
};
export type VectorStyleRequestMeta = MapFilters & {
export type VectorJoinSourceRequestMeta = Omit<VectorSourceRequestMeta, 'geogridPrecision'>;
export type VectorStyleRequestMeta = DataFilters & {
dynamicStyleFields: string[];
isTimeAware: boolean;
sourceQuery: MapQuery;
sourceQuery: Query;
timeFilters: TimeRange;
};
@ -107,7 +106,7 @@ export type VectorTileLayerMeta = {
};
// Partial because objects are justified downstream in constructors
export type DataMeta = Partial<
export type DataRequestMeta = Partial<
VectorSourceRequestMeta &
VectorJoinSourceRequestMeta &
VectorStyleRequestMeta &
@ -134,8 +133,8 @@ export type StyleMetaData = {
export type DataRequestDescriptor = {
dataId: string;
dataMetaAtStart?: DataMeta | null;
dataRequestMetaAtStart?: DataRequestMeta | null;
dataRequestToken?: symbol;
data?: object;
dataMeta?: DataMeta;
dataRequestMeta?: DataRequestMeta;
};

View file

@ -10,7 +10,6 @@
import { ReactNode } from 'react';
import { GeoJsonProperties } from 'geojson';
import { Geometry } from 'geojson';
import { Query } from '../../../../../src/plugins/data/common';
import { DRAW_SHAPE, ES_SPATIAL_RELATIONS } from '../constants';
export type MapExtent = {
@ -20,10 +19,6 @@ export type MapExtent = {
maxLat: number;
};
export type MapQuery = Query & {
queryLastTriggeredAt?: string;
};
export type MapCenter = {
lat: number;
lon: number;

View file

@ -42,6 +42,7 @@ export type AbstractESSourceDescriptor = AbstractSourceDescriptor & {
geoField?: string;
applyGlobalQuery: boolean;
applyGlobalTime: boolean;
applyForceRefresh: boolean;
};
type AbstractAggDescriptor = {

View file

@ -45,22 +45,28 @@ import {
} from './map_action_constants';
import { ILayer } from '../classes/layers/layer';
import { IVectorLayer } from '../classes/layers/vector_layer';
import { DataMeta, MapExtent, MapFilters } from '../../common/descriptor_types';
import { DataRequestMeta, MapExtent, DataFilters } from '../../common/descriptor_types';
import { DataRequestAbortError } from '../classes/util/data_request';
import { scaleBounds, turfBboxToBounds } from '../../common/elasticsearch_util';
const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1;
export type DataRequestContext = {
startLoading(dataId: string, requestToken: symbol, requestMeta?: DataMeta): void;
stopLoading(dataId: string, requestToken: symbol, data: object, resultsMeta?: DataMeta): void;
startLoading(dataId: string, requestToken: symbol, requestMeta?: DataRequestMeta): void;
stopLoading(
dataId: string,
requestToken: symbol,
data: object,
resultsMeta?: DataRequestMeta
): void;
onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void;
onJoinError(errorMessage: string): void;
updateSourceData(newData: unknown): void;
isRequestStillActive(dataId: string, requestToken: symbol): boolean;
registerCancelCallback(requestToken: symbol, callback: () => void): void;
dataFilters: MapFilters;
forceRefresh: boolean;
dataFilters: DataFilters;
forceRefreshDueToDrawing: boolean; // Boolean signaling data request triggered by a user updating layer features via drawing tools. When true, layer will re-load regardless of "source.applyForceRefresh" flag.
isForceRefresh: boolean; // Boolean signaling data request triggered by auto-refresh timer or user clicking refresh button. When true, layer will re-load only when "source.applyForceRefresh" flag is set to true.
};
export function clearDataRequests(layer: ILayer) {
@ -112,13 +118,14 @@ function getDataRequestContext(
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState,
layerId: string,
forceRefresh: boolean = false
forceRefreshDueToDrawing: boolean,
isForceRefresh: boolean
): DataRequestContext {
return {
dataFilters: getDataFilters(getState()),
startLoading: (dataId: string, requestToken: symbol, meta: DataMeta) =>
startLoading: (dataId: string, requestToken: symbol, meta: DataRequestMeta) =>
dispatch(startDataLoad(layerId, dataId, requestToken, meta)),
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataMeta) =>
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataRequestMeta) =>
dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)),
onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) =>
dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)),
@ -136,17 +143,18 @@ function getDataRequestContext(
},
registerCancelCallback: (requestToken: symbol, callback: () => void) =>
dispatch(registerCancelCallback(requestToken, callback)),
forceRefresh,
forceRefreshDueToDrawing,
isForceRefresh,
};
}
export function syncDataForAllLayers() {
export function syncDataForAllLayers(isForceRefresh: boolean) {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
) => {
const syncPromises = getLayerList(getState()).map((layer) => {
return dispatch(syncDataForLayer(layer));
return dispatch(syncDataForLayer(layer, isForceRefresh));
});
await Promise.all(syncPromises);
};
@ -162,19 +170,20 @@ function syncDataForAllJoinLayers() {
return 'hasJoins' in layer ? (layer as IVectorLayer).hasJoins() : false;
})
.map((layer) => {
return dispatch(syncDataForLayer(layer));
return dispatch(syncDataForLayer(layer, false));
});
await Promise.all(syncPromises);
};
}
export function syncDataForLayer(layer: ILayer, forceRefresh: boolean = false) {
export function syncDataForLayerDueToDrawing(layer: ILayer) {
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
const dataRequestContext = getDataRequestContext(
dispatch,
getState,
layer.getId(),
forceRefresh
true,
false
);
if (!layer.isVisible() || !layer.showAtZoomLevel(dataRequestContext.dataFilters.zoom)) {
return;
@ -183,14 +192,30 @@ export function syncDataForLayer(layer: ILayer, forceRefresh: boolean = false) {
};
}
export function syncDataForLayerId(layerId: string | null) {
export function syncDataForLayer(layer: ILayer, isForceRefresh: boolean) {
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
const dataRequestContext = getDataRequestContext(
dispatch,
getState,
layer.getId(),
false,
isForceRefresh
);
if (!layer.isVisible() || !layer.showAtZoomLevel(dataRequestContext.dataFilters.zoom)) {
return;
}
await layer.syncData(dataRequestContext);
};
}
export function syncDataForLayerId(layerId: string | null, isForceRefresh: boolean) {
return async (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
) => {
const layer = getLayerById(layerId, getState());
if (layer) {
dispatch(syncDataForLayer(layer));
dispatch(syncDataForLayer(layer, isForceRefresh));
}
};
}
@ -204,7 +229,12 @@ function setLayerDataLoadErrorStatus(layerId: string, errorMessage: string | nul
};
}
function startDataLoad(layerId: string, dataId: string, requestToken: symbol, meta: DataMeta) {
function startDataLoad(
layerId: string,
dataId: string,
requestToken: symbol,
meta: DataRequestMeta
) {
return (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
getState: () => MapStoreState
@ -237,7 +267,7 @@ function endDataLoad(
dataId: string,
requestToken: symbol,
data: object,
meta: DataMeta
meta: DataRequestMeta
) {
return (
dispatch: ThunkDispatch<MapStoreState, void, AnyAction>,
@ -342,7 +372,7 @@ export function fitToLayerExtent(layerId: string) {
if (targetLayer) {
try {
const bounds = await targetLayer.getBounds(
getDataRequestContext(dispatch, getState, layerId)
getDataRequestContext(dispatch, getState, layerId, false, false)
);
if (bounds) {
await dispatch(setGotoWithBounds(scaleBounds(bounds, FIT_TO_BOUNDS_SCALE_FACTOR)));
@ -374,7 +404,9 @@ export function fitToDataBounds(onNoBounds?: () => void) {
if (!(await layer.isFittable())) {
return null;
}
return layer.getBounds(getDataRequestContext(dispatch, getState, layer.getId()));
return layer.getBounds(
getDataRequestContext(dispatch, getState, layer.getId(), false, false)
);
});
let bounds;
@ -442,7 +474,7 @@ export function autoFitToBounds() {
// Ensure layer syncing occurs when setGotoWithBounds is not triggered.
function onNoBounds() {
if (localSetQueryCallId === lastSetQueryCallId) {
dispatch(syncDataForAllLayers());
dispatch(syncDataForAllLayers(false));
}
}
dispatch(fitToDataBounds(onNoBounds));

View file

@ -80,7 +80,7 @@ export function rollbackToTrackedLayerStateForSelectedLayer() {
// syncDataForLayer may not trigger endDataLoad if no re-fetch is required
dispatch(updateStyleMeta(layerId));
dispatch(syncDataForLayerId(layerId));
dispatch(syncDataForLayerId(layerId, false));
};
}
@ -149,7 +149,7 @@ export function addLayer(layerDescriptor: LayerDescriptor) {
type: ADD_LAYER,
layer: layerDescriptor,
});
dispatch(syncDataForLayerId(layerDescriptor.id));
dispatch(syncDataForLayerId(layerDescriptor.id, false));
const layer = createLayerInstance(layerDescriptor);
const features = await layer.getLicensedFeatures();
@ -226,7 +226,7 @@ export function setLayerVisibility(layerId: string, makeVisible: boolean) {
visibility: makeVisible,
});
if (makeVisible) {
dispatch(syncDataForLayerId(layerId));
dispatch(syncDataForLayerId(layerId, false));
}
};
}
@ -330,7 +330,7 @@ function updateMetricsProp(layerId: string, value: unknown) {
value,
});
await dispatch(updateStyleProperties(layerId, previousFields as IESAggField[]));
dispatch(syncDataForLayerId(layerId));
dispatch(syncDataForLayerId(layerId, false));
};
}
@ -356,7 +356,7 @@ export function updateSourceProp(
if (newLayerType) {
dispatch(updateLayerType(layerId, newLayerType));
}
dispatch(syncDataForLayerId(layerId));
dispatch(syncDataForLayerId(layerId, false));
};
}
@ -459,7 +459,7 @@ export function setLayerQuery(id: string, query: Query) {
newValue: query,
});
dispatch(syncDataForLayerId(id));
dispatch(syncDataForLayerId(id, false));
};
}
@ -563,7 +563,7 @@ export function updateLayerStyle(layerId: string, styleDescriptor: StyleDescript
dispatch(updateStyleMeta(layerId));
// Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling
dispatch(syncDataForLayerId(layerId));
dispatch(syncDataForLayerId(layerId, false));
};
}
@ -589,7 +589,7 @@ export function setJoinsForLayer(layer: ILayer, joins: JoinDescriptor[]) {
joins,
});
await dispatch(updateStyleProperties(layer.getId(), previousFields));
dispatch(syncDataForLayerId(layer.getId()));
dispatch(syncDataForLayerId(layer.getId(), false));
};
}

View file

@ -277,7 +277,6 @@ describe('map_actions', () => {
const query = {
language: 'kuery',
query: '',
queryLastTriggeredAt: '2020-08-14T15:07:12.276Z',
};
const timeFilters = { from: 'now-1y', to: 'now' };
const filters = [
@ -327,7 +326,6 @@ describe('map_actions', () => {
const newQuery = {
language: 'kuery',
query: 'foobar',
queryLastTriggeredAt: '2020-08-14T15:07:12.276Z',
};
const setQueryAction = await setQuery({
query: newQuery,
@ -384,7 +382,6 @@ describe('map_actions', () => {
});
await setQueryAction(dispatchMock, getStoreMock);
// Only checking calls length instead of calls because queryLastTriggeredAt changes on this run
expect(dispatchMock.mock.calls.length).toEqual(2);
});
});

View file

@ -51,7 +51,11 @@ import {
UPDATE_MAP_SETTING,
UPDATE_EDIT_STATE,
} from './map_action_constants';
import { autoFitToBounds, syncDataForAllLayers, syncDataForLayer } from './data_request_actions';
import {
autoFitToBounds,
syncDataForAllLayers,
syncDataForLayerDueToDrawing,
} from './data_request_actions';
import { addLayer, addLayerWithoutDataSync } from './layer_actions';
import { MapSettings } from '../reducers/map';
import { DrawState, MapCenterAndZoom, MapExtent, Timeslice } from '../../common/descriptor_types';
@ -172,7 +176,7 @@ export function mapExtentChanged(mapExtentState: MapExtentState) {
});
}
dispatch(syncDataForAllLayers());
dispatch(syncDataForAllLayers(false));
};
}
@ -212,10 +216,6 @@ export function clearGoto() {
return { type: CLEAR_GOTO };
}
function generateQueryTimestamp() {
return new Date().toISOString();
}
export function setQuery({
query,
timeFilters,
@ -240,11 +240,6 @@ export function setQuery({
getState: () => MapStoreState
) => {
const prevQuery = getQuery(getState());
const prevTriggeredAt =
prevQuery && prevQuery.queryLastTriggeredAt
? prevQuery.queryLastTriggeredAt
: generateQueryTimestamp();
const prevTimeFilters = getTimeFilters(getState());
function getNextTimeslice() {
@ -261,11 +256,7 @@ export function setQuery({
const nextQueryContext = {
timeFilters: timeFilters ? timeFilters : prevTimeFilters,
timeslice: getNextTimeslice(),
query: {
...(query ? query : prevQuery),
// ensure query changes to trigger re-fetch when "Refresh" clicked
queryLastTriggeredAt: forceRefresh ? generateQueryTimestamp() : prevTriggeredAt,
},
query: query ? query : prevQuery,
filters: filters ? filters : getFilters(getState()),
searchSessionId: searchSessionId ? searchSessionId : getSearchSessionId(getState()),
searchSessionMapBuffer,
@ -280,7 +271,7 @@ export function setQuery({
searchSessionMapBuffer: getSearchSessionMapBuffer(getState()),
};
if (_.isEqual(nextQueryContext, prevQueryContext)) {
if (!forceRefresh && _.isEqual(nextQueryContext, prevQueryContext)) {
// do nothing if query context has not changed
return;
}
@ -293,7 +284,7 @@ export function setQuery({
if (getMapSettings(getState()).autoFitToDataBounds) {
dispatch(autoFitToBounds());
} else {
await dispatch(syncDataForAllLayers());
await dispatch(syncDataForAllLayers(forceRefresh));
}
};
}
@ -372,7 +363,7 @@ export function addNewFeatureToIndex(geometry: Geometry | Position[]) {
try {
await layer.addFeature(geometry);
await dispatch(syncDataForLayer(layer, true));
await dispatch(syncDataForLayerDueToDrawing(layer));
} catch (e) {
getToasts().addError(e, {
title: i18n.translate('xpack.maps.mapActions.addFeatureError', {
@ -399,7 +390,7 @@ export function deleteFeatureFromIndex(featureId: string) {
}
try {
await layer.deleteFeature(featureId);
await dispatch(syncDataForLayer(layer, true));
await dispatch(syncDataForLayerDueToDrawing(layer));
} catch (e) {
getToasts().addError(e, {
title: i18n.translate('xpack.maps.mapActions.removeFeatureError', {

View file

@ -7,21 +7,22 @@
import sinon from 'sinon';
import { DataRequestContext } from '../../../actions';
import { DataMeta, MapFilters } from '../../../../common/descriptor_types';
import { DataRequestMeta, DataFilters } from '../../../../common/descriptor_types';
export class MockSyncContext implements DataRequestContext {
dataFilters: MapFilters;
dataFilters: DataFilters;
isRequestStillActive: (dataId: string, requestToken: symbol) => boolean;
onLoadError: (dataId: string, requestToken: symbol, errorMessage: string) => void;
registerCancelCallback: (requestToken: symbol, callback: () => void) => void;
startLoading: (dataId: string, requestToken: symbol, meta: DataMeta) => void;
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataMeta) => void;
startLoading: (dataId: string, requestToken: symbol, meta: DataRequestMeta) => void;
stopLoading: (dataId: string, requestToken: symbol, data: object, meta: DataRequestMeta) => void;
onJoinError: (errorMessage: string) => void;
updateSourceData: (newData: unknown) => void;
forceRefresh: boolean;
forceRefreshDueToDrawing: boolean;
isForceRefresh: boolean;
constructor({ dataFilters }: { dataFilters: Partial<MapFilters> }) {
const mapFilters: MapFilters = {
constructor({ dataFilters }: { dataFilters: Partial<DataFilters> }) {
const mapFilters: DataFilters = {
filters: [],
timeFilters: {
from: 'now',
@ -41,6 +42,7 @@ export class MockSyncContext implements DataRequestContext {
this.stopLoading = sinon.spy();
this.onJoinError = sinon.spy();
this.updateSourceData = sinon.spy();
this.forceRefresh = false;
this.forceRefreshDueToDrawing = false;
this.isForceRefresh = false;
}
}

View file

@ -63,6 +63,7 @@ describe('getSource', () => {
...documentSourceDescriptor,
applyGlobalQuery: false,
applyGlobalTime: false,
applyForceRefresh: false,
}),
layerDescriptor: BlendedVectorLayer.createDescriptor(
{
@ -86,6 +87,7 @@ describe('getSource', () => {
geoField: sourceDescriptor.geoField,
applyGlobalQuery: sourceDescriptor.applyGlobalQuery,
applyGlobalTime: sourceDescriptor.applyGlobalTime,
applyForceRefresh: sourceDescriptor.applyForceRefresh,
};
expect(abstractEsSourceDescriptor).toEqual({
type: sourceDescriptor.type,
@ -94,6 +96,7 @@ describe('getSource', () => {
indexPatternId: 'myIndexPattern',
applyGlobalQuery: false,
applyGlobalTime: false,
applyForceRefresh: false,
} as AbstractESSourceDescriptor);
});
});

View file

@ -60,6 +60,7 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle
});
clusterSourceDescriptor.applyGlobalQuery = documentSource.getApplyGlobalQuery();
clusterSourceDescriptor.applyGlobalTime = documentSource.getApplyGlobalTime();
clusterSourceDescriptor.applyForceRefresh = documentSource.getApplyForceRefresh();
clusterSourceDescriptor.metrics = [
{
type: AGG_TYPE.COUNT,
@ -290,16 +291,18 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
async syncData(syncContext: DataRequestContext) {
const dataRequestId = ACTIVE_COUNT_DATA_ID;
const requestToken = Symbol(`layer-active-count:${this.getId()}`);
const searchFilters: VectorSourceRequestMeta = await this._getSearchFilters(
const requestMeta: VectorSourceRequestMeta = await this._getVectorSourceRequestMeta(
syncContext.isForceRefresh,
syncContext.dataFilters,
this.getSource(),
this.getCurrentStyle()
);
const source = this.getSource();
const canSkipFetch = await canSkipSourceUpdate({
const canSkipSourceFetch = await canSkipSourceUpdate({
source,
prevDataRequest: this.getDataRequest(dataRequestId),
nextMeta: searchFilters,
nextRequestMeta: requestMeta,
extentAware: source.isFilterByMapBounds(),
getUpdateDueToTimeslice: (timeslice?: Timeslice) => {
return this._getUpdateDueToTimesliceFromSourceRequestMeta(source, timeslice);
@ -308,7 +311,7 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
let activeSource;
let activeStyle;
if (canSkipFetch) {
if (canSkipSourceFetch) {
// Even when source fetch is skipped, need to call super._syncData to sync StyleMeta and formatters
if (this._isClustered) {
activeSource = this._clusterSource;
@ -320,12 +323,12 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
} else {
let isSyncClustered;
try {
syncContext.startLoading(dataRequestId, requestToken, searchFilters);
syncContext.startLoading(dataRequestId, requestToken, requestMeta);
isSyncClustered = !(await this._documentSource.canLoadAllDocuments(
searchFilters,
requestMeta,
syncContext.registerCancelCallback.bind(null, requestToken)
));
syncContext.stopLoading(dataRequestId, requestToken, { isSyncClustered }, searchFilters);
syncContext.stopLoading(dataRequestId, requestToken, { isSyncClustered }, requestMeta);
} catch (error) {
if (!(error instanceof DataRequestAbortError) || !isSearchSourceAbortError(error)) {
syncContext.onLoadError(dataRequestId, requestToken, error.message);

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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import _ from 'lodash';
import type { Query } from 'src/plugins/data/common';
import { DataFilters, VectorSourceRequestMeta } from '../../../common/descriptor_types';
import { IVectorSource } from '../sources/vector_source';
import { ITermJoinSource } from '../sources/term_join_source';
export function buildVectorRequestMeta(
source: IVectorSource | ITermJoinSource,
fieldNames: string[],
dataFilters: DataFilters,
sourceQuery: Query | null | undefined,
isForceRefresh: boolean
): VectorSourceRequestMeta {
return {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom),
sourceQuery: sourceQuery ? sourceQuery : undefined,
applyGlobalQuery: source.getApplyGlobalQuery(),
applyGlobalTime: source.getApplyGlobalTime(),
sourceMeta: source.getSyncMeta(),
applyForceRefresh: source.isESSource() ? source.getApplyForceRefresh() : false,
isForceRefresh,
};
}

View file

@ -64,6 +64,7 @@ function createChoroplethLayerDescriptor({
metrics: [metricsDescriptor],
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
},
},
],
@ -148,6 +149,8 @@ export function createEsChoroplethLayerDescriptor({
scalingType: SCALING_TYPES.LIMIT,
tooltipProperties: [leftJoinField],
applyGlobalQuery: false,
applyGlobalTime: false,
applyForceRefresh: false,
}),
leftField: leftJoinField,
rightIndexPatternId,

View file

@ -92,6 +92,7 @@ export function createRegionMapLayerDescriptor({
metrics: [metricsDescriptor],
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
};
if (termsSize !== undefined) {
termSourceDescriptor.size = termsSize;

View file

@ -10,11 +10,12 @@ import { FeatureCollection } from 'geojson';
import { AbstractLayer } from '../layer';
import { HeatmapStyle } from '../../styles/heatmap/heatmap_style';
import { EMPTY_FEATURE_COLLECTION, LAYER_TYPE } from '../../../../common/constants';
import { HeatmapLayerDescriptor, MapQuery } from '../../../../common/descriptor_types';
import { HeatmapLayerDescriptor } from '../../../../common/descriptor_types';
import { ESGeoGridSource } from '../../sources/es_geo_grid_source';
import { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from '../vector_layer';
import { DataRequestContext } from '../../../actions';
import { DataRequestAbortError } from '../../util/data_request';
import { buildVectorRequestMeta } from '../build_vector_request_meta';
const SCALED_PROPERTY_NAME = '__kbn_heatmap_weight__'; // unique name to store scaled value for weighting
@ -94,21 +95,18 @@ export class HeatmapLayer extends AbstractLayer {
return;
}
const sourceQuery = this.getQuery() as MapQuery;
try {
await syncVectorSource({
layerId: this.getId(),
layerName: await this.getDisplayName(this.getSource()),
prevDataRequest: this.getSourceDataRequest(),
requestMeta: {
...syncContext.dataFilters,
fieldNames: this.getSource().getFieldNames(),
geogridPrecision: this.getSource().getGeoGridPrecision(syncContext.dataFilters.zoom),
sourceQuery: sourceQuery ? sourceQuery : undefined,
applyGlobalQuery: this.getSource().getApplyGlobalQuery(),
applyGlobalTime: this.getSource().getApplyGlobalTime(),
sourceMeta: this.getSource().getSyncMeta(),
},
requestMeta: buildVectorRequestMeta(
this.getSource(),
this.getSource().getFieldNames(),
syncContext.dataFilters,
this.getQuery(),
syncContext.isForceRefresh
),
syncContext,
source: this.getSource(),
getUpdateDueToTimeslice: () => {
@ -194,7 +192,7 @@ export class HeatmapLayer extends AbstractLayer {
layerId: this.getId(),
syncContext,
source: this.getSource(),
sourceQuery: this.getQuery() as MapQuery,
sourceQuery: this.getQuery(),
});
}

View file

@ -84,6 +84,7 @@ describe('cloneDescriptor', () => {
type: SOURCE_TYPES.ES_TERM_SOURCE,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
},
},
],

View file

@ -61,6 +61,7 @@ describe('createLayerDescriptor', () => {
type: 'avg',
},
],
applyForceRefresh: true,
term: 'client.geo.country_iso_code',
type: 'ES_TERM_SOURCE',
whereQuery: {
@ -201,6 +202,7 @@ describe('createLayerDescriptor', () => {
],
requestType: 'heatmap',
resolution: 'MOST_FINE',
applyForceRefresh: true,
type: 'ES_GEO_GRID',
},
style: {
@ -245,6 +247,7 @@ describe('createLayerDescriptor', () => {
],
requestType: 'point',
resolution: 'MOST_FINE',
applyForceRefresh: true,
type: 'ES_GEO_GRID',
},
style: {

View file

@ -179,6 +179,7 @@ export function createLayerDescriptor({
whereQuery: apmSourceQuery,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
},
},
],

View file

@ -47,6 +47,7 @@ describe('createLayerDescriptor', () => {
geoField: 'client.geo.location',
id: '12345',
indexPatternId: 'id',
applyForceRefresh: true,
scalingType: 'TOP_HITS',
sortField: '',
sortOrder: 'desc',
@ -156,6 +157,7 @@ describe('createLayerDescriptor', () => {
geoField: 'server.geo.location',
id: '12345',
indexPatternId: 'id',
applyForceRefresh: true,
scalingType: 'TOP_HITS',
sortField: '',
sortOrder: 'desc',
@ -274,6 +276,7 @@ describe('createLayerDescriptor', () => {
type: 'sum',
},
],
applyForceRefresh: true,
sourceGeoField: 'client.geo.location',
type: 'ES_PEW_PEW',
},
@ -386,6 +389,7 @@ describe('createLayerDescriptor', () => {
geoField: 'source.geo.location',
id: '12345',
indexPatternId: 'id',
applyForceRefresh: true,
scalingType: 'TOP_HITS',
sortField: '',
sortOrder: 'desc',
@ -495,6 +499,7 @@ describe('createLayerDescriptor', () => {
geoField: 'destination.geo.location',
id: '12345',
indexPatternId: 'id',
applyForceRefresh: true,
scalingType: 'TOP_HITS',
sortField: '',
sortOrder: 'desc',
@ -613,6 +618,7 @@ describe('createLayerDescriptor', () => {
type: 'sum',
},
],
applyForceRefresh: true,
sourceGeoField: 'source.geo.location',
type: 'ES_PEW_PEW',
},
@ -724,6 +730,7 @@ describe('createLayerDescriptor', () => {
filterByMapBounds: true,
geoField: 'client.geo.location',
id: '12345',
applyForceRefresh: true,
indexPatternId: 'id',
scalingType: 'TOP_HITS',
sortField: '',
@ -835,6 +842,7 @@ describe('createLayerDescriptor', () => {
id: '12345',
indexPatternId: 'id',
scalingType: 'TOP_HITS',
applyForceRefresh: true,
sortField: '',
sortOrder: 'desc',
tooltipProperties: [
@ -952,6 +960,7 @@ describe('createLayerDescriptor', () => {
type: 'sum',
},
],
applyForceRefresh: true,
sourceGeoField: 'client.geo.location',
type: 'ES_PEW_PEW',
},

View file

@ -38,7 +38,6 @@ import {
} from '../../../../common/descriptor_types';
import { MVTSingleLayerVectorSourceConfig } from '../../sources/mvt_single_layer_vector_source/types';
import { canSkipSourceUpdate } from '../../util/can_skip_fetch';
import { isRefreshOnlyQuery } from '../../util/is_refresh_only_query';
import { CustomIconAndTooltipContent } from '../layer';
export class TiledVectorLayer extends VectorLayer {
@ -113,9 +112,11 @@ export class TiledVectorLayer extends VectorLayer {
stopLoading,
onLoadError,
dataFilters,
isForceRefresh,
}: DataRequestContext) {
const requestToken: symbol = Symbol(`layer-${this.getId()}-${SOURCE_DATA_REQUEST_ID}`);
const searchFilters: VectorSourceRequestMeta = await this._getSearchFilters(
const requestMeta: VectorSourceRequestMeta = await this._getVectorSourceRequestMeta(
isForceRefresh,
dataFilters,
this.getSource(),
this._style as IVectorStyle
@ -132,7 +133,7 @@ export class TiledVectorLayer extends VectorLayer {
extentAware: false, // spatial extent knowledge is already fully automated by tile-loading based on pan-zooming
source: this.getSource(),
prevDataRequest,
nextMeta: searchFilters,
nextRequestMeta: requestMeta,
getUpdateDueToTimeslice: (timeslice?: Timeslice) => {
// TODO use meta features to determine if tiles already contain features for timeslice.
return true;
@ -145,18 +146,17 @@ export class TiledVectorLayer extends VectorLayer {
}
}
startLoading(SOURCE_DATA_REQUEST_ID, requestToken, searchFilters);
startLoading(SOURCE_DATA_REQUEST_ID, requestToken, requestMeta);
try {
const prevMeta = prevDataRequest ? prevDataRequest.getMeta() : undefined;
const prevData = prevDataRequest
? (prevDataRequest.getData() as MVTSingleLayerVectorSourceConfig)
: undefined;
const urlToken =
!prevData || isRefreshOnlyQuery(prevMeta ? prevMeta.query : undefined, searchFilters.query)
!prevData || (requestMeta.isForceRefresh && requestMeta.applyForceRefresh)
? uuid()
: prevData.urlToken;
const newUrlTemplateAndMeta = await this._source.getUrlTemplateWithMeta(searchFilters);
const newUrlTemplateAndMeta = await this._source.getUrlTemplateWithMeta(requestMeta);
let urlTemplate;
if (newUrlTemplateAndMeta.refreshTokenParamName) {

View file

@ -7,6 +7,7 @@
import { FeatureCollection } from 'geojson';
import type { Map as MbMap } from '@kbn/mapbox-gl';
import type { Query } from 'src/plugins/data/common';
import {
EMPTY_FEATURE_COLLECTION,
SOURCE_BOUNDS_DATA_REQUEST_ID,
@ -14,9 +15,8 @@ import {
VECTOR_SHAPE_TYPE,
} from '../../../../common/constants';
import {
DataMeta,
DataRequestMeta,
MapExtent,
MapQuery,
Timeslice,
VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
@ -77,15 +77,17 @@ export async function syncVectorSource({
} = syncContext;
const dataRequestId = SOURCE_DATA_REQUEST_ID;
const requestToken = Symbol(`${layerId}-${dataRequestId}`);
const canSkipFetch = syncContext.forceRefresh
const canSkipFetch = syncContext.forceRefreshDueToDrawing
? false
: await canSkipSourceUpdate({
source,
prevDataRequest,
nextMeta: requestMeta,
nextRequestMeta: requestMeta,
extentAware: source.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
if (canSkipFetch) {
return {
refreshed: false,
@ -113,11 +115,11 @@ export async function syncVectorSource({
) {
layerFeatureCollection.features.push(...getCentroidFeatures(layerFeatureCollection));
}
const responseMeta: DataMeta = meta ? { ...meta } : {};
const responseMeta: DataRequestMeta = meta ? { ...meta } : {};
if (requestMeta.applyGlobalTime && (await source.isTimeAware())) {
const timesiceMaskField = await source.getTimesliceMaskFieldName();
if (timesiceMaskField) {
responseMeta.timesiceMaskField = timesiceMaskField;
const timesliceMaskField = await source.getTimesliceMaskFieldName();
if (timesliceMaskField) {
responseMeta.timesliceMaskField = timesliceMaskField;
}
}
stopLoading(dataRequestId, requestToken, layerFeatureCollection, responseMeta);
@ -142,7 +144,7 @@ export async function getVectorSourceBounds({
layerId: string;
syncContext: DataRequestContext;
source: IVectorSource;
sourceQuery: MapQuery | null;
sourceQuery: Query | null;
}): Promise<MapExtent | null> {
const { startLoading, stopLoading, registerCancelCallback, dataFilters } = syncContext;

View file

@ -11,6 +11,7 @@ import type {
AnyLayer as MbLayer,
GeoJSONSource as MbGeoJSONSource,
} from '@kbn/mapbox-gl';
import type { Query } from 'src/plugins/data/common';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry, Position } from 'geojson';
import _ from 'lodash';
import { EuiIcon } from '@elastic/eui';
@ -48,14 +49,13 @@ import {
} from '../../util/mb_filter_expressions';
import {
DynamicStylePropertyOptions,
MapFilters,
MapQuery,
DataFilters,
StyleMetaDescriptor,
Timeslice,
VectorJoinSourceRequestMeta,
VectorLayerDescriptor,
VectorSourceRequestMeta,
VectorStyleRequestMeta,
VectorJoinSourceRequestMeta,
} from '../../../../common/descriptor_types';
import { ISource } from '../../sources/source';
import { IVectorSource } from '../../sources/vector_source';
@ -70,6 +70,7 @@ import { PropertiesMap } from '../../../../common/elasticsearch_util';
import { ITermJoinSource } from '../../sources/term_join_source';
import { addGeoJsonMbSource, getVectorSourceBounds, syncVectorSource } from './utils';
import { JoinState, performInnerJoins } from './perform_inner_joins';
import { buildVectorRequestMeta } from '../build_vector_request_meta';
export interface VectorLayerArguments {
source: IVectorSource;
@ -266,7 +267,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
layerId: this.getId(),
syncContext,
source: this.getSource(),
sourceQuery: this.getQuery() as MapQuery,
sourceQuery: this.getQuery(),
});
}
@ -332,29 +333,31 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
onLoadError,
registerCancelCallback,
dataFilters,
isForceRefresh,
}: { join: InnerJoin } & DataRequestContext): Promise<JoinState> {
const joinSource = join.getRightJoinSource();
const sourceDataId = join.getSourceDataRequestId();
const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`);
const searchFilters: VectorJoinSourceRequestMeta = {
...dataFilters,
fieldNames: joinSource.getFieldNames(),
sourceQuery: joinSource.getWhereQuery(),
applyGlobalQuery: joinSource.getApplyGlobalQuery(),
applyGlobalTime: joinSource.getApplyGlobalTime(),
sourceMeta: joinSource.getSyncMeta(),
};
const prevDataRequest = this.getDataRequest(sourceDataId);
const joinRequestMeta: VectorJoinSourceRequestMeta = buildVectorRequestMeta(
joinSource,
joinSource.getFieldNames(),
dataFilters,
joinSource.getWhereQuery(),
isForceRefresh
) as VectorJoinSourceRequestMeta;
const prevDataRequest = this.getDataRequest(sourceDataId);
const canSkipFetch = await canSkipSourceUpdate({
source: joinSource,
prevDataRequest,
nextMeta: searchFilters,
nextRequestMeta: joinRequestMeta,
extentAware: false, // join-sources are term-aggs that are spatially unaware (e.g. ESTermSource/TableSource).
getUpdateDueToTimeslice: () => {
return true;
},
});
if (canSkipFetch) {
return {
dataHasChanged: false,
@ -364,10 +367,10 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
}
try {
startLoading(sourceDataId, requestToken, searchFilters);
startLoading(sourceDataId, requestToken, joinRequestMeta);
const leftSourceName = await this._source.getDisplayName();
const propertiesMap = await joinSource.getPropertiesMap(
searchFilters,
joinRequestMeta,
leftSourceName,
join.getLeftField().getName(),
registerCancelCallback.bind(null, requestToken)
@ -396,8 +399,9 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
return await Promise.all(joinSyncs);
}
async _getSearchFilters(
dataFilters: MapFilters,
async _getVectorSourceRequestMeta(
isForceRefresh: boolean,
dataFilters: DataFilters,
source: IVectorSource,
style: IVectorStyle
): Promise<VectorSourceRequestMeta> {
@ -411,17 +415,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
if (timesliceMaskFieldName) {
fieldNames.push(timesliceMaskFieldName);
}
const sourceQuery = this.getQuery() as MapQuery;
return {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom),
sourceQuery: sourceQuery ? sourceQuery : undefined,
applyGlobalQuery: source.getApplyGlobalQuery(),
applyGlobalTime: source.getApplyGlobalTime(),
sourceMeta: source.getSyncMeta(),
};
return buildVectorRequestMeta(source, fieldNames, dataFilters, this.getQuery(), isForceRefresh);
}
async _syncSourceStyleMeta(
@ -429,7 +423,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
source: IVectorSource,
style: IVectorStyle
) {
const sourceQuery = this.getQuery() as MapQuery;
const sourceQuery = this.getQuery();
return this._syncStyleMeta({
source,
style,
@ -481,7 +475,7 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
dataRequestId: string;
dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>;
source: IVectorSource | ITermJoinSource;
sourceQuery?: MapQuery;
sourceQuery?: Query;
style: IVectorStyle;
} & DataRequestContext) {
if (!source.isESSource() || dynamicStyleProps.length === 0) {
@ -641,7 +635,12 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
layerId: this.getId(),
layerName: await this.getDisplayName(source),
prevDataRequest: this.getSourceDataRequest(),
requestMeta: await this._getSearchFilters(syncContext.dataFilters, source, style),
requestMeta: await this._getVectorSourceRequestMeta(
syncContext.isForceRefresh,
syncContext.dataFilters,
source,
style
),
syncContext,
source,
getUpdateDueToTimeslice: (timeslice?: Timeslice) => {
@ -995,9 +994,9 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
}
const prevMeta = this.getSourceDataRequest()?.getMeta();
return prevMeta !== undefined && prevMeta.timesiceMaskField !== undefined
return prevMeta !== undefined && prevMeta.timesliceMaskField !== undefined
? {
timesiceMaskField: prevMeta.timesiceMaskField,
timesliceMaskField: prevMeta.timesliceMaskField,
timeslice,
}
: undefined;

View file

@ -7,7 +7,7 @@
import { ITileLayerArguments } from '../tile_layer/tile_layer';
import { SOURCE_TYPES } from '../../../../common/constants';
import { MapFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
import { DataFilters, XYZTMSSourceDescriptor } from '../../../../common/descriptor_types';
import { ITMSSource, AbstractTMSSource } from '../../sources/tms_source';
import { ILayer } from '../layer';
import { VectorTileLayer } from './vector_tile_layer';
@ -63,7 +63,7 @@ describe('VectorTileLayer', () => {
onLoadError: (requestId: string, token: string, message: string) => {
actualErrorMessage = message;
},
dataFilters: ({ foo: 'bar' } as unknown) as MapFilters,
dataFilters: ({ foo: 'bar' } as unknown) as DataFilters,
} as unknown) as DataRequestContext;
await layer.syncData(mockContext);

View file

@ -40,6 +40,7 @@ class TestESAggSource extends AbstractESAggSource {
metrics,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
},
[]
);

View file

@ -155,15 +155,16 @@ describe('ESGeoGridSource', () => {
extent,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
fieldNames: [],
buffer: extent,
sourceQuery: {
query: '',
language: 'KQL',
queryLastTriggeredAt: '2019-04-25T20:53:22.331Z',
},
sourceMeta: null,
zoom: 0,
isForceRefresh: false,
};
describe('getGeoJsonWithMeta', () => {
@ -315,7 +316,7 @@ describe('ESGeoGridSource', () => {
expect(urlTemplateWithMeta.minSourceZoom).toBe(0);
expect(urlTemplateWithMeta.maxSourceZoom).toBe(24);
expect(urlTemplateWithMeta.urlTemplate).toEqual(
"rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point"
"rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point"
);
});
@ -327,7 +328,7 @@ describe('ESGeoGridSource', () => {
expect(
urlTemplateWithMeta.urlTemplate.startsWith(
"rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point&searchSessionId=1"
"rootdir/api/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=undefined&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:())),'1':('0':size,'1':0),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:())),'5':('0':query,'1':(language:KQL,query:'')),'6':('0':aggs,'1':(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:bar))),geotile_grid:(bounds:!n,field:bar,precision:!n,shard_size:65535,size:65535))))))&requestType=heatmap&geoFieldType=geo_point&searchSessionId=1"
)
).toBe(true);

View file

@ -20,7 +20,7 @@ describe('getSourceTooltipContent', () => {
const sourceDataRequest = new DataRequest({
data: {},
dataId: 'source',
dataMeta: {
dataRequestMeta: {
areResultsTrimmed: false,
areEntitiesTrimmed: false,
entityCount: 70,
@ -39,7 +39,7 @@ describe('getSourceTooltipContent', () => {
const sourceDataRequest = new DataRequest({
data: {},
dataId: 'source',
dataMeta: {
dataRequestMeta: {
areResultsTrimmed: true,
areEntitiesTrimmed: true,
entityCount: 1000,
@ -58,7 +58,7 @@ describe('getSourceTooltipContent', () => {
const sourceDataRequest = new DataRequest({
data: {},
dataId: 'source',
dataMeta: {
dataRequestMeta: {
areResultsTrimmed: false,
areEntitiesTrimmed: false,
entityCount: 70,
@ -77,7 +77,7 @@ describe('getSourceTooltipContent', () => {
const sourceDataRequest = new DataRequest({
data: {},
dataId: 'source',
dataMeta: {
dataRequestMeta: {
areResultsTrimmed: true,
areEntitiesTrimmed: true,
entityCount: 1000,

View file

@ -41,6 +41,7 @@ test('Should create layer descriptor', () => {
geoField: 'myGeoField',
id: '12345',
indexPatternId: 'myIndexPattern',
applyForceRefresh: true,
scalingType: 'CLUSTERS',
sortField: '',
sortOrder: 'desc',

View file

@ -102,11 +102,12 @@ describe('ESSearchSource', () => {
sourceQuery: {
query: 'tooltipField: foobar',
language: 'KQL',
queryLastTriggeredAt: '2019-04-25T20:53:22.331Z',
},
sourceMeta: null,
applyGlobalQuery: true,
applyGlobalTime: true,
applyForceRefresh: true,
isForceRefresh: false,
};
it('Should only include required props', async () => {
@ -116,7 +117,7 @@ describe('ESSearchSource', () => {
});
const urlTemplateWithMeta = await esSearchSource.getUrlTemplateWithMeta(searchFilters);
expect(urlTemplateWithMeta.urlTemplate).toBe(
`rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape`
`rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape`
);
});
@ -130,7 +131,7 @@ describe('ESSearchSource', () => {
searchSessionId: '1',
});
expect(urlTemplateWithMeta.urlTemplate).toBe(
`rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar',queryLastTriggeredAt:'2019-04-25T20:53:22.331Z')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape&searchSessionId=1`
`rootdir/api/maps/mvt/getTile/{z}/{x}/{y}.pbf?geometryFieldName=bar&index=foobar-title-*&requestBody=(foobar:ES_DSL_PLACEHOLDER,params:('0':('0':index,'1':(fields:(),title:'foobar-title-*')),'1':('0':size,'1':1000),'2':('0':filter,'1':!()),'3':('0':query),'4':('0':index,'1':(fields:(),title:'foobar-title-*')),'5':('0':query,'1':(language:KQL,query:'tooltipField: foobar')),'6':('0':fieldsFromSource,'1':!(tooltipField,styleField)),'7':('0':source,'1':!(tooltipField,styleField))))&geoFieldType=geo_shape&searchSessionId=1`
);
});
});

View file

@ -48,7 +48,7 @@ import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants';
import { ESDocField } from '../../fields/es_doc_field';
import { registerSource } from '../source_registry';
import {
DataMeta,
DataRequestMeta,
ESSearchSourceDescriptor,
Timeslice,
VectorSourceRequestMeta,
@ -853,7 +853,7 @@ export class ESSearchSource extends AbstractESSource implements ITiledSingleLaye
return indexPattern.timeFieldName ? indexPattern.timeFieldName : null;
}
getUpdateDueToTimeslice(prevMeta: DataMeta, timeslice?: Timeslice): boolean {
getUpdateDueToTimeslice(prevMeta: DataRequestMeta, timeslice?: Timeslice): boolean {
if (this._isTopHits() || this._descriptor.scalingType === SCALING_TYPES.MVT) {
return true;
}

View file

@ -8,7 +8,8 @@
import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
import { Filter, IndexPatternField, IndexPattern, ISearchSource } from 'src/plugins/data/public';
import { AbstractVectorSource, BoundsFilters } from '../vector_source';
import type { Query } from 'src/plugins/data/common';
import { AbstractVectorSource, BoundsRequestMeta } from '../vector_source';
import {
getAutocompleteService,
getIndexPatternService,
@ -26,7 +27,6 @@ import {
AbstractSourceDescriptor,
DynamicStylePropertyOptions,
MapExtent,
MapQuery,
VectorJoinSourceRequestMeta,
VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
@ -60,7 +60,7 @@ export interface IESSource extends IVectorSource {
style: IVectorStyle;
dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>;
registerCancelCallback: (callback: () => void) => void;
sourceQuery?: MapQuery;
sourceQuery?: Query;
timeFilters: TimeRange;
searchSessionId?: string;
}): Promise<object>;
@ -88,6 +88,8 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
typeof descriptor.applyGlobalQuery !== 'undefined' ? descriptor.applyGlobalQuery : true,
applyGlobalTime:
typeof descriptor.applyGlobalTime !== 'undefined' ? descriptor.applyGlobalTime : true,
applyForceRefresh:
typeof descriptor.applyForceRefresh !== 'undefined' ? descriptor.applyForceRefresh : true,
};
}
@ -108,11 +110,11 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
return this._descriptor.applyGlobalTime;
}
isFieldAware(): boolean {
return true;
getApplyForceRefresh(): boolean {
return this._descriptor.applyForceRefresh;
}
isRefreshTimerAware(): boolean {
isFieldAware(): boolean {
return true;
}
@ -197,7 +199,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
}
async makeSearchSource(
searchFilters: VectorSourceRequestMeta | VectorJoinSourceRequestMeta | BoundsFilters,
searchFilters: VectorSourceRequestMeta | VectorJoinSourceRequestMeta | BoundsRequestMeta,
limit: number,
initialSearchContext?: object
): Promise<ISearchSource> {
@ -253,7 +255,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
boundsFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
const searchSource = await this.makeSearchSource(boundsFilters, 0);
@ -421,7 +423,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
style: IVectorStyle;
dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>;
registerCancelCallback: (callback: () => void) => void;
sourceQuery?: MapQuery;
sourceQuery?: Query;
timeFilters: TimeRange;
searchSessionId?: string;
}): Promise<object> {

View file

@ -6,7 +6,7 @@
*/
import { GeoJsonFileSource } from './geojson_file_source';
import { BoundsFilters } from '../vector_source';
import { BoundsRequestMeta } from '../vector_source';
import { FIELD_ORIGIN } from '../../../../common/constants';
describe('GeoJsonFileSource', () => {
@ -20,7 +20,7 @@ describe('GeoJsonFileSource', () => {
it('should get null bounds', async () => {
const geojsonFileSource = new GeoJsonFileSource({});
expect(
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {})
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsRequestMeta, () => {})
).toEqual(null);
});
@ -51,7 +51,7 @@ describe('GeoJsonFileSource', () => {
expect(geojsonFileSource.isBoundsAware()).toBe(true);
expect(
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {})
await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsRequestMeta, () => {})
).toEqual({
maxLat: 3,
maxLon: 2,

View file

@ -6,7 +6,7 @@
*/
import { Feature, FeatureCollection } from 'geojson';
import { AbstractVectorSource, BoundsFilters, GeoJsonWithMeta } from '../vector_source';
import { AbstractVectorSource, BoundsRequestMeta, GeoJsonWithMeta } from '../vector_source';
import { EMPTY_FEATURE_COLLECTION, FIELD_ORIGIN, SOURCE_TYPES } from '../../../../common/constants';
import {
InlineFieldDescriptor,
@ -103,7 +103,7 @@ export class GeoJsonFileSource extends AbstractVectorSource {
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
boundsFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
const featureCollection = (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection;

View file

@ -10,7 +10,7 @@ import uuid from 'uuid/v4';
import React from 'react';
import { GeoJsonProperties, Geometry, Position } from 'geojson';
import { AbstractSource, ImmutableSourceProperty, SourceEditorArgs } from '../source';
import { BoundsFilters, GeoJsonWithMeta } from '../vector_source';
import { BoundsRequestMeta, GeoJsonWithMeta } from '../vector_source';
import { ITiledSingleLayerVectorSource } from '../tiled_single_layer_vector_source';
import {
FIELD_ORIGIN,
@ -190,7 +190,7 @@ export class MVTSingleLayerVectorSource
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
boundsFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
return null;

View file

@ -16,7 +16,7 @@ import { FieldFormatter, LAYER_TYPE, MAX_ZOOM, MIN_ZOOM } from '../../../common/
import {
AbstractSourceDescriptor,
Attribution,
DataMeta,
DataRequestMeta,
Timeslice,
} from '../../../common/descriptor_types';
import { LICENSED_FEATURES } from '../../licensed_features';
@ -47,7 +47,6 @@ export interface ISource {
isFilterByMapBounds(): boolean;
isGeoGridPrecisionAware(): boolean;
isQueryAware(): boolean;
isRefreshTimerAware(): boolean;
isTimeAware(): Promise<boolean>;
getImmutableProperties(): Promise<ImmutableSourceProperty[]>;
getAttributionProvider(): (() => Promise<Attribution[]>) | null;
@ -60,6 +59,7 @@ export interface ISource {
getFieldNames(): string[];
getApplyGlobalQuery(): boolean;
getApplyGlobalTime(): boolean;
getApplyForceRefresh(): boolean;
getIndexPatternIds(): string[];
getQueryableIndexPatternIds(): string[];
getGeoGridPrecision(zoom: number): number;
@ -69,7 +69,7 @@ export interface ISource {
getMinZoom(): number;
getMaxZoom(): number;
getLicensedFeatures(): Promise<LICENSED_FEATURES[]>;
getUpdateDueToTimeslice(prevMeta: DataMeta, timeslice?: Timeslice): boolean;
getUpdateDueToTimeslice(prevMeta: DataRequestMeta, timeslice?: Timeslice): boolean;
}
export class AbstractSource implements ISource {
@ -115,10 +115,6 @@ export class AbstractSource implements ISource {
return false;
}
isRefreshTimerAware(): boolean {
return false;
}
isGeoGridPrecisionAware(): boolean {
return false;
}
@ -143,6 +139,10 @@ export class AbstractSource implements ISource {
return false;
}
getApplyForceRefresh(): boolean {
return false;
}
getIndexPatternIds(): string[] {
return [];
}
@ -201,7 +201,7 @@ export class AbstractSource implements ISource {
return [];
}
getUpdateDueToTimeslice(prevMeta: DataMeta, timeslice?: Timeslice): boolean {
getUpdateDueToTimeslice(prevMeta: DataRequestMeta, timeslice?: Timeslice): boolean {
return true;
}
}

View file

@ -5,11 +5,11 @@
* 2.0.
*/
import type { Query } from 'src/plugins/data/common';
import { TableSource } from './table_source';
import { FIELD_ORIGIN } from '../../../../common/constants';
import {
MapFilters,
MapQuery,
DataFilters,
VectorJoinSourceRequestMeta,
VectorSourceSyncMeta,
} from '../../../../common/descriptor_types';
@ -178,12 +178,12 @@ describe('TableSource', () => {
try {
await tableSource.getGeoJsonWithMeta(
'foobar',
({} as unknown) as MapFilters & {
({} as unknown) as DataFilters & {
applyGlobalQuery: boolean;
applyGlobalTime: boolean;
fieldNames: string[];
geogridPrecision?: number;
sourceQuery?: MapQuery;
sourceQuery?: Query;
sourceMeta: VectorSourceSyncMeta;
},
() => {},

View file

@ -6,11 +6,11 @@
*/
import uuid from 'uuid';
import type { Query } from 'src/plugins/data/common';
import { FIELD_ORIGIN, SOURCE_TYPES, VECTOR_SHAPE_TYPE } from '../../../../common/constants';
import {
MapExtent,
MapFilters,
MapQuery,
DataFilters,
TableSourceDescriptor,
VectorJoinSourceRequestMeta,
VectorSourceSyncMeta,
@ -19,10 +19,9 @@ import { Adapters } from '../../../../../../../src/plugins/inspector/common/adap
import { ITermJoinSource } from '../term_join_source';
import { BucketProperties, PropertiesMap } from '../../../../common/elasticsearch_util';
import { IField } from '../../fields/field';
import { Query } from '../../../../../../../src/plugins/data/common/query';
import {
AbstractVectorSource,
BoundsFilters,
BoundsRequestMeta,
GeoJsonWithMeta,
IVectorSource,
SourceTooltipConfig,
@ -156,7 +155,7 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
boundsFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
return null;
@ -187,12 +186,12 @@ export class TableSource extends AbstractVectorSource implements ITermJoinSource
// Could be useful to implement, e.g. to preview raw csv data
async getGeoJsonWithMeta(
layerName: string,
searchFilters: MapFilters & {
searchFilters: DataFilters & {
applyGlobalQuery: boolean;
applyGlobalTime: boolean;
fieldNames: string[];
geogridPrecision?: number;
sourceQuery?: MapQuery;
sourceQuery?: Query;
sourceMeta: VectorSourceSyncMeta;
},
registerCancelCallback: (callback: () => void) => void,

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import type { Query } from 'src/plugins/data/common';
import { FeatureCollection, GeoJsonProperties, Geometry, Position } from 'geojson';
import { Filter, TimeRange } from 'src/plugins/data/public';
import { VECTOR_SHAPE_TYPE } from '../../../../common/constants';
@ -14,7 +15,6 @@ import { IField } from '../../fields/field';
import {
ESSearchSourceResponseMeta,
MapExtent,
MapQuery,
Timeslice,
VectorSourceRequestMeta,
VectorSourceSyncMeta,
@ -34,12 +34,12 @@ export interface GeoJsonWithMeta {
meta?: GeoJsonFetchMeta;
}
export interface BoundsFilters {
export interface BoundsRequestMeta {
applyGlobalQuery: boolean;
applyGlobalTime: boolean;
filters: Filter[];
query?: MapQuery;
sourceQuery?: MapQuery;
query?: Query;
sourceQuery?: Query;
timeFilters: TimeRange;
timeslice?: Timeslice;
}
@ -47,7 +47,7 @@ export interface BoundsFilters {
export interface IVectorSource extends ISource {
getTooltipProperties(properties: GeoJsonProperties): Promise<ITooltipProperty[]>;
getBoundsForFilters(
boundsFilters: BoundsFilters,
layerDataFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null>;
getGeoJsonWithMeta(
@ -103,7 +103,7 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc
}
async getBoundsForFilters(
boundsFilters: BoundsFilters,
boundsFilters: BoundsRequestMeta,
registerCancelCallback: (callback: () => void) => void
): Promise<MapExtent | null> {
return null;

View file

@ -7,6 +7,8 @@
import { canSkipSourceUpdate, updateDueToExtent } from './can_skip_fetch';
import { DataRequest } from './data_request';
import { Filter } from 'src/plugins/data/common';
import { ISource } from '../sources/source';
describe('updateDueToExtent', () => {
it('should be false when buffers are the same', async () => {
@ -91,9 +93,6 @@ describe('canSkipSourceUpdate', () => {
isTimeAware: () => {
return false;
},
isRefreshTimerAware: () => {
return false;
},
isFilterByMapBounds: () => {
return false;
},
@ -107,11 +106,10 @@ describe('canSkipSourceUpdate', () => {
return false;
},
};
const prevFilters = [];
const prevFilters: Filter[] = [];
const prevQuery = {
language: 'kuery',
query: 'machine.os.keyword : "win 7"',
queryLastTriggeredAt: '2019-04-25T20:53:22.331Z',
};
describe('applyGlobalQuery is false', () => {
@ -119,7 +117,7 @@ describe('canSkipSourceUpdate', () => {
const prevDataRequest = new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
@ -128,16 +126,16 @@ describe('canSkipSourceUpdate', () => {
});
it('can skip update when filter changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: [prevQuery],
filters: [({} as unknown) as Filter],
query: prevQuery,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -146,7 +144,7 @@ describe('canSkipSourceUpdate', () => {
});
it('can skip update when query changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
@ -156,9 +154,9 @@ describe('canSkipSourceUpdate', () => {
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -166,20 +164,19 @@ describe('canSkipSourceUpdate', () => {
expect(canSkipUpdate).toBe(true);
});
it('can not skip update when query is refreshed', async () => {
const nextMeta = {
it('Should not skip refresh update when applyForceRefresh is true', async () => {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
queryLastTriggeredAt: 'sometime layer when Refresh button is clicked',
},
query: prevQuery,
isForceRefresh: true,
applyForceRefresh: true,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -187,17 +184,37 @@ describe('canSkipSourceUpdate', () => {
expect(canSkipUpdate).toBe(false);
});
it('Should skip refresh update when applyForceRefresh is false', async () => {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
isForceRefresh: true,
applyForceRefresh: false,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
expect(canSkipUpdate).toBe(true);
});
it('can not skip update when applyGlobalQuery changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: !prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -211,7 +228,7 @@ describe('canSkipSourceUpdate', () => {
const prevDataRequest = new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
@ -220,16 +237,16 @@ describe('canSkipSourceUpdate', () => {
});
it('can not skip update when filter changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: [prevQuery],
filters: [({} as unknown) as Filter],
query: prevQuery,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -238,7 +255,7 @@ describe('canSkipSourceUpdate', () => {
});
it('can not skip update when query changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
@ -248,9 +265,9 @@ describe('canSkipSourceUpdate', () => {
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -259,19 +276,18 @@ describe('canSkipSourceUpdate', () => {
});
it('can not skip update when query is refreshed', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: prevApplyGlobalQuery,
filters: prevFilters,
query: {
...prevQuery,
queryLastTriggeredAt: 'sometime layer when Refresh button is clicked',
},
query: prevQuery,
isForceRefresh: true,
applyForceRefresh: true,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -280,16 +296,16 @@ describe('canSkipSourceUpdate', () => {
});
it('can not skip update when applyGlobalQuery changes', async () => {
const nextMeta = {
const nextRequestMeta = {
applyGlobalQuery: !prevApplyGlobalQuery,
filters: prevFilters,
query: prevQuery,
};
const canSkipUpdate = await canSkipSourceUpdate({
source: queryAwareSourceMock,
source: (queryAwareSourceMock as unknown) as ISource,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware: queryAwareSourceMock.isFilterByMapBounds(),
getUpdateDueToTimeslice,
});
@ -305,9 +321,6 @@ describe('canSkipSourceUpdate', () => {
isTimeAware: () => {
return true;
},
isRefreshTimerAware: () => {
return false;
},
isFilterByMapBounds: () => {
return false;
},
@ -326,15 +339,15 @@ describe('canSkipSourceUpdate', () => {
describe('applyGlobalTime', () => {
it('can not skip update when applyGlobalTime changes', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: createSourceMock(),
source: (createSourceMock() as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: false,
},
extentAware: false,
@ -346,15 +359,15 @@ describe('canSkipSourceUpdate', () => {
it('can skip update when applyGlobalTime does not change', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: createSourceMock(),
source: (createSourceMock() as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
},
extentAware: false,
@ -368,10 +381,10 @@ describe('canSkipSourceUpdate', () => {
describe('timeFilters', () => {
it('can not skip update when time range changes', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: createSourceMock(),
source: (createSourceMock() as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-15m',
@ -380,7 +393,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -396,10 +409,10 @@ describe('canSkipSourceUpdate', () => {
it('can skip update when time range does not change', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: createSourceMock(),
source: (createSourceMock() as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-15m',
@ -408,7 +421,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-15m',
@ -424,10 +437,10 @@ describe('canSkipSourceUpdate', () => {
it('can skip update when time range changes but applyGlobalTime is false', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: createSourceMock(),
source: (createSourceMock() as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: false,
timeFilters: {
from: 'now-15m',
@ -436,7 +449,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: false,
timeFilters: {
from: 'now-7d',
@ -455,10 +468,10 @@ describe('canSkipSourceUpdate', () => {
const mockSource = createSourceMock();
it('can not skip update when timeslice changes (undefined => provided)', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: mockSource,
source: (mockSource as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -467,7 +480,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -487,10 +500,10 @@ describe('canSkipSourceUpdate', () => {
it('can not skip update when timeslice changes', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: mockSource,
source: (mockSource as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -503,7 +516,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -523,10 +536,10 @@ describe('canSkipSourceUpdate', () => {
it('can not skip update when timeslice changes (provided => undefined)', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: mockSource,
source: (mockSource as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -539,7 +552,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -555,10 +568,10 @@ describe('canSkipSourceUpdate', () => {
it('can skip update when timeslice does not change', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: mockSource,
source: (mockSource as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -571,7 +584,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: true,
timeFilters: {
from: 'now-7d',
@ -591,10 +604,10 @@ describe('canSkipSourceUpdate', () => {
it('can skip update when timeslice changes but applyGlobalTime is false', async () => {
const canSkipUpdate = await canSkipSourceUpdate({
source: mockSource,
source: (mockSource as unknown) as ISource,
prevDataRequest: new DataRequest({
dataId: SOURCE_DATA_REQUEST_ID,
dataMeta: {
dataRequestMeta: {
applyGlobalTime: false,
timeFilters: {
from: 'now-7d',
@ -607,7 +620,7 @@ describe('canSkipSourceUpdate', () => {
},
data: {},
}),
nextMeta: {
nextRequestMeta: {
applyGlobalTime: false,
timeFilters: {
from: 'now-7d',

View file

@ -8,15 +8,14 @@
import _ from 'lodash';
import turfBboxPolygon from '@turf/bbox-polygon';
import turfBooleanContains from '@turf/boolean-contains';
import { isRefreshOnlyQuery } from './is_refresh_only_query';
import { ISource } from '../sources/source';
import { DataMeta, Timeslice } from '../../../common/descriptor_types';
import { DataRequestMeta, Timeslice } from '../../../common/descriptor_types';
import { DataRequest } from './data_request';
const SOURCE_UPDATE_REQUIRED = true;
const NO_SOURCE_UPDATE_REQUIRED = false;
export function updateDueToExtent(prevMeta: DataMeta = {}, nextMeta: DataMeta = {}) {
export function updateDueToExtent(prevMeta: DataRequestMeta = {}, nextMeta: DataRequestMeta = {}) {
const { buffer: previousBuffer } = prevMeta;
const { buffer: newBuffer } = nextMeta;
@ -54,30 +53,28 @@ export function updateDueToExtent(prevMeta: DataMeta = {}, nextMeta: DataMeta =
export async function canSkipSourceUpdate({
source,
prevDataRequest,
nextMeta,
nextRequestMeta,
extentAware,
getUpdateDueToTimeslice,
}: {
source: ISource;
prevDataRequest: DataRequest | undefined;
nextMeta: DataMeta;
nextRequestMeta: DataRequestMeta;
extentAware: boolean;
getUpdateDueToTimeslice: (timeslice?: Timeslice) => boolean;
}): Promise<boolean> {
const mustForceRefresh = nextRequestMeta.isForceRefresh && nextRequestMeta.applyForceRefresh;
if (mustForceRefresh) {
// Cannot skip
return false;
}
const timeAware = await source.isTimeAware();
const refreshTimerAware = await source.isRefreshTimerAware();
const isFieldAware = source.isFieldAware();
const isQueryAware = source.isQueryAware();
const isGeoGridPrecisionAware = source.isGeoGridPrecisionAware();
if (
!timeAware &&
!refreshTimerAware &&
!extentAware &&
!isFieldAware &&
!isQueryAware &&
!isGeoGridPrecisionAware
) {
if (!timeAware && !extentAware && !isFieldAware && !isQueryAware && !isGeoGridPrecisionAware) {
return !!prevDataRequest && prevDataRequest.hasDataOrRequestInProgress();
}
@ -93,26 +90,18 @@ export async function canSkipSourceUpdate({
let updateDueToTime = false;
let updateDueToTimeslice = false;
if (timeAware) {
updateDueToApplyGlobalTime = prevMeta.applyGlobalTime !== nextMeta.applyGlobalTime;
if (nextMeta.applyGlobalTime) {
updateDueToTime = !_.isEqual(prevMeta.timeFilters, nextMeta.timeFilters);
if (!_.isEqual(prevMeta.timeslice, nextMeta.timeslice)) {
updateDueToTimeslice = getUpdateDueToTimeslice(nextMeta.timeslice);
updateDueToApplyGlobalTime = prevMeta.applyGlobalTime !== nextRequestMeta.applyGlobalTime;
if (nextRequestMeta.applyGlobalTime) {
updateDueToTime = !_.isEqual(prevMeta.timeFilters, nextRequestMeta.timeFilters);
if (!_.isEqual(prevMeta.timeslice, nextRequestMeta.timeslice)) {
updateDueToTimeslice = getUpdateDueToTimeslice(nextRequestMeta.timeslice);
}
}
}
let updateDueToRefreshTimer = false;
if (refreshTimerAware && nextMeta.refreshTimerLastTriggeredAt) {
updateDueToRefreshTimer = !_.isEqual(
prevMeta.refreshTimerLastTriggeredAt,
nextMeta.refreshTimerLastTriggeredAt
);
}
let updateDueToFields = false;
if (isFieldAware) {
updateDueToFields = !_.isEqual(prevMeta.fieldNames, nextMeta.fieldNames);
updateDueToFields = !_.isEqual(prevMeta.fieldNames, nextRequestMeta.fieldNames);
}
let updateDueToQuery = false;
@ -120,42 +109,41 @@ export async function canSkipSourceUpdate({
let updateDueToSourceQuery = false;
let updateDueToApplyGlobalQuery = false;
if (isQueryAware) {
updateDueToApplyGlobalQuery = prevMeta.applyGlobalQuery !== nextMeta.applyGlobalQuery;
updateDueToSourceQuery = !_.isEqual(prevMeta.sourceQuery, nextMeta.sourceQuery);
updateDueToApplyGlobalQuery = prevMeta.applyGlobalQuery !== nextRequestMeta.applyGlobalQuery;
updateDueToSourceQuery = !_.isEqual(prevMeta.sourceQuery, nextRequestMeta.sourceQuery);
if (nextMeta.applyGlobalQuery) {
updateDueToQuery = !_.isEqual(prevMeta.query, nextMeta.query);
updateDueToFilters = !_.isEqual(prevMeta.filters, nextMeta.filters);
} else {
// Global filters and query are not applied to layer search request so no re-fetch required.
// Exception is "Refresh" query.
updateDueToQuery = isRefreshOnlyQuery(prevMeta.query, nextMeta.query);
if (nextRequestMeta.applyGlobalQuery) {
updateDueToQuery = !_.isEqual(prevMeta.query, nextRequestMeta.query);
updateDueToFilters = !_.isEqual(prevMeta.filters, nextRequestMeta.filters);
}
}
let updateDueToSearchSessionId = false;
if (timeAware || isQueryAware) {
updateDueToSearchSessionId = prevMeta.searchSessionId !== nextMeta.searchSessionId;
if ((timeAware || isQueryAware) && nextRequestMeta.applyForceRefresh) {
// If the force-refresh flag is turned off, we should ignore refreshes on the dashboard-context
updateDueToSearchSessionId = prevMeta.searchSessionId !== nextRequestMeta.searchSessionId;
}
let updateDueToPrecisionChange = false;
let updateDueToExtentChange = false;
if (isGeoGridPrecisionAware) {
updateDueToPrecisionChange = !_.isEqual(prevMeta.geogridPrecision, nextMeta.geogridPrecision);
updateDueToPrecisionChange = !_.isEqual(
prevMeta.geogridPrecision,
nextRequestMeta.geogridPrecision
);
}
if (extentAware) {
updateDueToExtentChange = updateDueToExtent(prevMeta, nextMeta);
updateDueToExtentChange = updateDueToExtent(prevMeta, nextRequestMeta);
}
const updateDueToSourceMetaChange = !_.isEqual(prevMeta.sourceMeta, nextMeta.sourceMeta);
const updateDueToSourceMetaChange = !_.isEqual(prevMeta.sourceMeta, nextRequestMeta.sourceMeta);
return (
!updateDueToApplyGlobalTime &&
!updateDueToTime &&
!updateDueToTimeslice &&
!updateDueToRefreshTimer &&
!updateDueToExtentChange &&
!updateDueToFields &&
!updateDueToQuery &&
@ -173,7 +161,7 @@ export function canSkipStyleMetaUpdate({
nextMeta,
}: {
prevDataRequest: DataRequest | undefined;
nextMeta: DataMeta;
nextMeta: DataRequestMeta;
}): boolean {
if (!prevDataRequest) {
return false;
@ -208,7 +196,7 @@ export function canSkipFormattersUpdate({
nextMeta,
}: {
prevDataRequest: DataRequest | undefined;
nextMeta: DataMeta;
nextMeta: DataRequestMeta;
}): boolean {
if (!prevDataRequest) {
return false;

View file

@ -7,7 +7,7 @@
/* eslint-disable max-classes-per-file */
import { DataRequestDescriptor, DataMeta } from '../../../common/descriptor_types';
import { DataRequestDescriptor, DataRequestMeta } from '../../../common/descriptor_types';
export class DataRequest {
private readonly _descriptor: DataRequestDescriptor;
@ -26,11 +26,11 @@ export class DataRequest {
return !!this._descriptor.dataRequestToken;
}
getMeta(): DataMeta {
if (this._descriptor.dataMetaAtStart) {
return this._descriptor.dataMetaAtStart;
} else if (this._descriptor.dataMeta) {
return this._descriptor.dataMeta;
getMeta(): DataRequestMeta {
if (this._descriptor.dataRequestMetaAtStart) {
return this._descriptor.dataRequestMetaAtStart;
} else if (this._descriptor.dataRequestMeta) {
return this._descriptor.dataRequestMeta;
} else {
return {};
}

View file

@ -1,24 +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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { MapQuery } from '../../../common/descriptor_types';
// 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: MapQuery | undefined,
newQuery: MapQuery | undefined
): boolean {
if (!prevQuery || !newQuery) {
return false;
}
return (
prevQuery.queryLastTriggeredAt !== newQuery.queryLastTriggeredAt &&
prevQuery.language === newQuery.language &&
prevQuery.query === newQuery.query
);
}

View file

@ -15,7 +15,7 @@ import {
import { Timeslice } from '../../../common/descriptor_types';
export interface TimesliceMaskConfig {
timesiceMaskField: string;
timesliceMaskField: string;
timeslice: Timeslice;
}
@ -34,15 +34,15 @@ function getFilterExpression(
}
if (timesliceMaskConfig) {
allFilters.push(['has', timesliceMaskConfig.timesiceMaskField]);
allFilters.push(['has', timesliceMaskConfig.timesliceMaskField]);
allFilters.push([
'>=',
['get', timesliceMaskConfig.timesiceMaskField],
['get', timesliceMaskConfig.timesliceMaskField],
timesliceMaskConfig.timeslice.from,
]);
allFilters.push([
'<',
['get', timesliceMaskConfig.timesiceMaskField],
['get', timesliceMaskConfig.timesliceMaskField],
timesliceMaskConfig.timeslice.to,
]);
}

View file

@ -0,0 +1,42 @@
/*
* 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 React from 'react';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
interface Props {
applyForceRefresh: boolean;
setApplyForceRefresh: (applyGlobalTime: boolean) => void;
}
export function ForceRefreshCheckbox({ applyForceRefresh, setApplyForceRefresh }: Props) {
const onRespondToForceRefreshChange = (event: EuiSwitchEvent) => {
setApplyForceRefresh(event.target.checked);
};
return (
<EuiFormRow display="columnCompressedSwitch">
<EuiToolTip
position="top"
content={i18n.translate('xpack.maps.filterEditor.applyForceRefreshTooltip', {
defaultMessage: `When enabled, results are narrowed by search bar`,
})}
>
<EuiSwitch
label={i18n.translate('xpack.maps.filterEditor.applyForceRefreshLabel', {
defaultMessage: `Apply global refresh to layer data`,
})}
checked={applyForceRefresh}
onChange={onRespondToForceRefreshChange}
data-test-subj="mapLayerPanelRespondToForceRefreshCheckbox"
compressed
/>
</EuiToolTip>
</EuiFormRow>
);
}

View file

@ -6,7 +6,8 @@
*/
import React from 'react';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
interface Props {
applyGlobalQuery: boolean;
@ -21,13 +22,20 @@ export function GlobalFilterCheckbox({ applyGlobalQuery, label, setApplyGlobalQu
return (
<EuiFormRow display="columnCompressedSwitch">
<EuiSwitch
label={label}
checked={applyGlobalQuery}
onChange={onApplyGlobalQueryChange}
data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox"
compressed
/>
<EuiToolTip
position="top"
content={i18n.translate('xpack.maps.filterEditor.applyGlobalFilterHelp', {
defaultMessage: 'When enabled, results narrowed by global search',
})}
>
<EuiSwitch
label={label}
checked={applyGlobalQuery}
onChange={onApplyGlobalQueryChange}
data-test-subj="mapLayerPanelApplyGlobalQueryCheckbox"
compressed
/>
</EuiToolTip>
</EuiFormRow>
);
}

View file

@ -6,8 +6,8 @@
*/
import React from 'react';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui';
import { EuiFormRow, EuiSwitch, EuiSwitchEvent, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
interface Props {
applyGlobalTime: boolean;
label: string;
@ -21,13 +21,20 @@ export function GlobalTimeCheckbox({ applyGlobalTime, label, setApplyGlobalTime
return (
<EuiFormRow display="columnCompressedSwitch">
<EuiSwitch
label={label}
checked={applyGlobalTime}
onChange={onApplyGlobalTimeChange}
data-test-subj="mapLayerPanelApplyGlobalTimeCheckbox"
compressed
/>
<EuiToolTip
position="top"
content={i18n.translate('xpack.maps.filterEditor.applyGlobalTimeHelp', {
defaultMessage: 'When enabled, results narrowed by global time filter',
})}
>
<EuiSwitch
label={label}
checked={applyGlobalTime}
onChange={onApplyGlobalTimeChange}
data-test-subj="mapLayerPanelApplyGlobalTimeCheckbox"
compressed
/>
</EuiToolTip>
</EuiFormRow>
);
}

View file

@ -15,8 +15,10 @@ import {
EuiSpacer,
EuiText,
EuiTextColor,
EuiTextAlign,
EuiButtonEmpty,
EuiHorizontalRule,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
@ -27,6 +29,7 @@ import { getIndexPatternService, getData } from '../../../kibana_services';
import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox';
import { GlobalTimeCheckbox } from '../../../components/global_time_checkbox';
import { ILayer } from '../../../classes/layers/layer';
import { ForceRefreshCheckbox } from '../../../components/force_refresh_checkbox';
export interface Props {
layer: ILayer;
@ -113,6 +116,10 @@ export class FilterEditor extends Component<Props, State> {
this.props.updateSourceProp(this.props.layer.getId(), 'applyGlobalTime', applyGlobalTime);
};
_onRespondToForceRefreshChange = (applyForceRefresh: boolean) => {
this.props.updateSourceProp(this.props.layer.getId(), 'applyForceRefresh', applyForceRefresh);
};
_renderQueryPopover() {
const layerQuery = this.props.layer.getQuery();
const { SearchBar } = getData().ui;
@ -153,7 +160,7 @@ export class FilterEditor extends Component<Props, State> {
const query = this.props.layer.getQuery();
if (!query || !query.query) {
return (
<EuiText size="s" textAlign="center">
<EuiText size="s">
<p>
<EuiTextColor color="subdued">
<FormattedMessage
@ -169,7 +176,6 @@ export class FilterEditor extends Component<Props, State> {
return (
<Fragment>
<EuiCodeBlock paddingSize="s">{query.query}</EuiCodeBlock>
<EuiSpacer size="m" />
</Fragment>
);
@ -183,7 +189,7 @@ export class FilterEditor extends Component<Props, State> {
defaultMessage: 'Edit filter',
})
: i18n.translate('xpack.maps.layerPanel.filterEditor.addFilterButtonLabel', {
defaultMessage: 'Add filter',
defaultMessage: 'Set filter',
});
const openButtonIcon = query && query.query ? 'pencil' : 'plusInCircleFilled';
@ -209,6 +215,7 @@ export class FilterEditor extends Component<Props, State> {
setApplyGlobalTime={this._onApplyGlobalTimeChange}
/>
) : null;
return (
<Fragment>
<EuiTitle size="xs">
@ -222,12 +229,15 @@ export class FilterEditor extends Component<Props, State> {
<EuiSpacer size="m" />
{this._renderQuery()}
<EuiTextAlign textAlign="center">{this._renderQueryPopover()}</EuiTextAlign>
<EuiFlexGroup direction={'row'} wrap={false} component={'span'}>
<EuiFlexItem grow={1}>{this._renderQueryPopover()}</EuiFlexItem>
<EuiFlexItem grow={6}>{this._renderQuery()}</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<EuiHorizontalRule size="full" margin="s" />
<GlobalFilterCheckbox
label={i18n.translate('xpack.maps.filterEditor.applyGlobalQueryCheckboxLabel', {
defaultMessage: `Apply global filter to layer data`,
@ -237,6 +247,10 @@ export class FilterEditor extends Component<Props, State> {
/>
{globalTimeCheckbox}
<ForceRefreshCheckbox
applyForceRefresh={this.props.layer.getSource().getApplyForceRefresh()}
setApplyForceRefresh={this._onRespondToForceRefreshChange}
/>
</Fragment>
);
}

View file

@ -7,7 +7,7 @@
jest.mock('../../actions', () => ({}));
import { DataMeta, DataRequestDescriptor } from '../../../common/descriptor_types';
import { DataRequestMeta, DataRequestDescriptor } from '../../../common/descriptor_types';
import {
getDataRequest,
setDataRequest,
@ -157,7 +157,7 @@ describe('startDataRequest', () => {
const REQUEST_TOKEN = Symbol('request');
const DATA_META_AT_START = {
prop1: 'value',
} as DataMeta;
} as DataRequestMeta;
test('Should return unmodified state if layer not found', () => {
const state = ({
@ -186,7 +186,7 @@ describe('startDataRequest', () => {
__dataRequests: [
{
dataId: 'source',
dataMetaAtStart: DATA_META_AT_START,
dataRequestMetaAtStart: DATA_META_AT_START,
dataRequestToken: REQUEST_TOKEN,
},
],
@ -204,7 +204,7 @@ describe('startDataRequest', () => {
__dataRequests: [
{
dataId: 'source',
dataMetaAtStart: { prop1: 'old value' },
dataRequestMetaAtStart: { prop1: 'old value' },
dataRequestToken: Symbol('request'),
},
],
@ -219,7 +219,7 @@ describe('startDataRequest', () => {
__dataRequests: [
{
dataId: 'source',
dataMetaAtStart: DATA_META_AT_START,
dataRequestMetaAtStart: DATA_META_AT_START,
dataRequestToken: REQUEST_TOKEN,
},
],
@ -270,7 +270,7 @@ describe('stopDataRequest', () => {
{
dataId: 'source',
dataRequestToken: REQUEST_TOKEN,
dataMetaAtStart: { requestProp1: 'request' },
dataRequestMetaAtStart: { requestProp1: 'request' },
data: { prop1: 'old data ' },
},
],
@ -278,7 +278,7 @@ describe('stopDataRequest', () => {
],
} as unknown) as MapState;
const stateClone = _.cloneDeep(state);
const reponseMeta = { responseProp1: 'response' } as DataMeta;
const reponseMeta = { responseProp1: 'response' } as DataRequestMeta;
const data = { prop1: 'new data' };
expect(stopDataRequest(state, 'layer1', 'source', REQUEST_TOKEN, reponseMeta, data)).toEqual({
layerList: [
@ -287,9 +287,9 @@ describe('stopDataRequest', () => {
__dataRequests: [
{
dataId: 'source',
dataMeta: { requestProp1: 'request', responseProp1: 'response' },
dataRequestMeta: { requestProp1: 'request', responseProp1: 'response' },
data: { prop1: 'new data' },
dataMetaAtStart: undefined,
dataRequestMetaAtStart: undefined,
dataRequestToken: undefined,
},
],

View file

@ -7,7 +7,7 @@
import { SOURCE_DATA_REQUEST_ID } from '../../../common/constants';
import { findLayerById, setLayer } from './layer_utils';
import { DataMeta, DataRequestDescriptor } from '../../../common/descriptor_types';
import { DataRequestMeta, DataRequestDescriptor } from '../../../common/descriptor_types';
import { MapState } from './types';
export function startDataRequest(
@ -15,7 +15,7 @@ export function startDataRequest(
layerId: string,
dataRequestId: string,
requestToken: symbol,
requestMeta: DataMeta
requestMeta: DataRequestMeta
): MapState {
const layerDescriptor = findLayerById(state, layerId);
if (!layerDescriptor) {
@ -30,7 +30,7 @@ export function startDataRequest(
: {
dataId: dataRequestId,
};
dataRequest.dataMetaAtStart = requestMeta;
dataRequest.dataRequestMetaAtStart = requestMeta;
dataRequest.dataRequestToken = requestToken;
return setDataRequest(state, layerId, dataRequest);
}
@ -49,7 +49,7 @@ export function stopDataRequest(
layerId: string,
dataRequestId: string,
requestToken: symbol,
responseMeta?: DataMeta,
responseMeta?: DataRequestMeta,
data?: object
): MapState {
const dataRequest = getDataRequest(state, layerId, dataRequestId, requestToken);
@ -57,11 +57,11 @@ export function stopDataRequest(
? setDataRequest(state, layerId, {
...dataRequest,
data,
dataMeta: {
...(dataRequest.dataMetaAtStart ? dataRequest.dataMetaAtStart : {}),
dataRequestMeta: {
...(dataRequest.dataRequestMetaAtStart ? dataRequest.dataRequestMetaAtStart : {}),
...(responseMeta ? responseMeta : {}),
},
dataMetaAtStart: undefined,
dataRequestMetaAtStart: undefined,
dataRequestToken: undefined,
})
: state;

View file

@ -75,7 +75,6 @@ export const DEFAULT_MAP_STATE: MapState = {
timeslice: undefined,
query: undefined,
filters: [],
refreshTimerLastTriggeredAt: undefined,
drawState: undefined,
editState: undefined,
},

View file

@ -7,6 +7,7 @@
/* eslint-disable @typescript-eslint/consistent-type-definitions */
import type { Query } from 'src/plugins/data/common';
import {
DrawState,
EditState,
@ -14,7 +15,6 @@ import {
LayerDescriptor,
MapCenter,
MapExtent,
MapQuery,
Timeslice,
TooltipState,
} from '../../../common/descriptor_types';
@ -39,9 +39,8 @@ export type MapContext = Partial<MapViewContext> & {
};
timeFilters?: TimeRange;
timeslice?: Timeslice;
query?: MapQuery;
query?: Query;
filters: Filter[];
refreshTimerLastTriggeredAt?: string;
drawState?: DrawState;
editState?: EditState;
searchSessionId?: string;

View file

@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
import { AppLeaveAction, AppMountParameters } from 'kibana/public';
import { Adapters } from 'src/plugins/embeddable/public';
import { Subscription } from 'rxjs';
import type { Query, Filter, TimeRange, IndexPattern } from 'src/plugins/data/common';
import {
getData,
getCoreChrome,
@ -30,10 +31,6 @@ import {
} from '../url_state';
import {
esFilters,
Filter,
Query,
TimeRange,
IndexPattern,
SavedQuery,
QueryStateChange,
QueryState,
@ -41,7 +38,6 @@ import {
import { MapContainer } from '../../../connected_components/map_container';
import { getIndexPatternsFromIds } from '../../../index_pattern_util';
import { getTopNavConfig } from '../top_nav_config';
import { MapQuery } from '../../../../common/descriptor_types';
import { goToSpecifiedPath } from '../../../render_app';
import { MapSavedObjectAttributes } from '../../../../common/map_saved_object_type';
import { getFullPath, APP_ID } from '../../../../common/constants';
@ -87,7 +83,7 @@ export interface Props {
}) => void;
timeFilters: TimeRange;
isSaveDisabled: boolean;
query: MapQuery | undefined;
query: Query | undefined;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
}

View file

@ -402,7 +402,7 @@ export class SavedMap {
isPaused: getTimeFilter().getRefreshInterval().pause,
interval: getTimeFilter().getRefreshInterval().value,
},
query: _.omit(getQuery(state), 'queryLastTriggeredAt'),
query: getQuery(state),
filters: getFilters(state),
settings: getMapSettings(state),
});

View file

@ -58,7 +58,6 @@ describe('getDataFilters', () => {
const mapZoom = 4;
const timeFilters = { to: '2001-01-01', from: '2001-12-31' };
const timeslice = undefined;
const refreshTimerLastTriggeredAt = '2001-01-01T00:00:00';
const query = undefined;
const filters: Filter[] = [];
const searchSessionId = '12345';
@ -77,7 +76,6 @@ describe('getDataFilters', () => {
mapZoom,
timeFilters,
timeslice,
refreshTimerLastTriggeredAt,
query,
filters,
searchSessionId,
@ -94,7 +92,6 @@ describe('getDataFilters', () => {
mapZoom,
timeFilters,
timeslice,
refreshTimerLastTriggeredAt,
query,
filters,
searchSessionId,

View file

@ -9,6 +9,7 @@ import { createSelector } from 'reselect';
import { FeatureCollection } from 'geojson';
import _ from 'lodash';
import { Adapters } from 'src/plugins/inspector/public';
import type { Query } from 'src/plugins/data/common';
import { TileLayer } from '../classes/layers/tile_layer/tile_layer';
// @ts-ignore
import { VectorTileLayer } from '../classes/layers/vector_tile_layer/vector_tile_layer';
@ -45,7 +46,6 @@ import {
LayerDescriptor,
MapCenter,
MapExtent,
MapQuery,
TooltipState,
VectorLayerDescriptor,
} from '../../common/descriptor_types';
@ -179,7 +179,7 @@ export const getTimeFilters = ({ map }: MapStoreState): TimeRange =>
export const getTimeslice = ({ map }: MapStoreState) => map.mapState.timeslice;
export const getQuery = ({ map }: MapStoreState): MapQuery | undefined => map.mapState.query;
export const getQuery = ({ map }: MapStoreState): Query | undefined => map.mapState.query;
export const getFilters = ({ map }: MapStoreState): Filter[] => map.mapState.filters;
@ -201,9 +201,6 @@ export const getDrawState = ({ map }: MapStoreState): DrawState | undefined =>
export const getEditState = ({ map }: MapStoreState): EditState | undefined =>
map.mapState.editState;
export const getRefreshTimerLastTriggeredAt = ({ map }: MapStoreState): string | undefined =>
map.mapState.refreshTimerLastTriggeredAt;
function getLayerDescriptor(state: MapStoreState, layerId: string) {
const layerListRaw = getLayerListRaw(state);
return layerListRaw.find((layer) => layer.id === layerId);
@ -225,7 +222,6 @@ export const getDataFilters = createSelector(
getMapZoom,
getTimeFilters,
getTimeslice,
getRefreshTimerLastTriggeredAt,
getQuery,
getFilters,
getSearchSessionId,
@ -237,7 +233,6 @@ export const getDataFilters = createSelector(
mapZoom,
timeFilters,
timeslice,
refreshTimerLastTriggeredAt,
query,
filters,
searchSessionId,
@ -250,7 +245,6 @@ export const getDataFilters = createSelector(
zoom: mapZoom,
timeFilters,
timeslice,
refreshTimerLastTriggeredAt,
query,
filters,
searchSessionId,