mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
merge with master (#61097)
This commit is contained in:
parent
96d235fc9e
commit
f680edc443
15 changed files with 141 additions and 53 deletions
|
@ -5,25 +5,41 @@
|
|||
*/
|
||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
||||
|
||||
import { Query } from './map_descriptor';
|
||||
|
||||
type Extent = {
|
||||
maxLat: number;
|
||||
maxLon: number;
|
||||
minLat: number;
|
||||
minLon: number;
|
||||
};
|
||||
|
||||
// Global map state passed to every layer.
|
||||
export type MapFilters = {
|
||||
buffer: unknown;
|
||||
extent: unknown;
|
||||
buffer: Extent; // extent with additional buffer
|
||||
extent: Extent; // map viewport
|
||||
filters: unknown[];
|
||||
query: unknown;
|
||||
query: Query;
|
||||
refreshTimerLastTriggeredAt: string;
|
||||
timeFilters: unknown;
|
||||
zoom: number;
|
||||
};
|
||||
|
||||
export type VectorLayerRequestMeta = MapFilters & {
|
||||
export type VectorSourceRequestMeta = MapFilters & {
|
||||
applyGlobalQuery: boolean;
|
||||
fieldNames: string[];
|
||||
geogridPrecision: number;
|
||||
sourceQuery: unknown;
|
||||
sourceQuery: Query;
|
||||
sourceMeta: unknown;
|
||||
};
|
||||
|
||||
export type VectorStyleRequestMeta = MapFilters & {
|
||||
dynamicStyleFields: string[];
|
||||
isTimeAware: boolean;
|
||||
sourceQuery: Query;
|
||||
timeFilters: unknown;
|
||||
};
|
||||
|
||||
export type ESSearchSourceResponseMeta = {
|
||||
areResultsTrimmed?: boolean;
|
||||
sourceType?: string;
|
||||
|
@ -35,7 +51,9 @@ export type ESSearchSourceResponseMeta = {
|
|||
};
|
||||
|
||||
// Partial because objects are justified downstream in constructors
|
||||
export type DataMeta = Partial<VectorLayerRequestMeta> & Partial<ESSearchSourceResponseMeta>;
|
||||
export type DataMeta = Partial<VectorSourceRequestMeta> &
|
||||
Partial<VectorStyleRequestMeta> &
|
||||
Partial<ESSearchSourceResponseMeta>;
|
||||
|
||||
export type DataRequestDescriptor = {
|
||||
dataId: string;
|
||||
|
|
13
x-pack/legacy/plugins/maps/common/map_descriptor.ts
Normal file
13
x-pack/legacy/plugins/maps/common/map_descriptor.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
||||
|
||||
export type Query = {
|
||||
language: string;
|
||||
query: string;
|
||||
queryLastTriggeredAt: string;
|
||||
};
|
|
@ -23,7 +23,6 @@ import {
|
|||
FIELD_ORIGIN,
|
||||
} from '../../common/constants';
|
||||
import { ESGeoGridSource } from './sources/es_geo_grid_source/es_geo_grid_source';
|
||||
// @ts-ignore
|
||||
import { canSkipSourceUpdate } from './util/can_skip_fetch';
|
||||
import { IVectorLayer, VectorLayerArguments } from './vector_layer';
|
||||
import { IESSource } from './sources/es_source';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { AbstractVectorSource } from './vector_source';
|
||||
import { IVectorSource } from './vector_source';
|
||||
import { IndexPattern, SearchSource } from '../../../../../../../src/plugins/data/public';
|
||||
import { VectorLayerRequestMeta } from '../../../common/data_request_descriptor_types';
|
||||
import { VectorSourceRequestMeta } from '../../../common/data_request_descriptor_types';
|
||||
|
||||
export interface IESSource extends IVectorSource {
|
||||
getId(): string;
|
||||
|
@ -16,7 +16,7 @@ export interface IESSource extends IVectorSource {
|
|||
getGeoFieldName(): string;
|
||||
getMaxResultWindow(): Promise<number>;
|
||||
makeSearchSource(
|
||||
searchFilters: VectorLayerRequestMeta,
|
||||
searchFilters: VectorSourceRequestMeta,
|
||||
limit: number,
|
||||
initialSearchContext?: object
|
||||
): Promise<SearchSource>;
|
||||
|
@ -29,7 +29,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
|
|||
getGeoFieldName(): string;
|
||||
getMaxResultWindow(): Promise<number>;
|
||||
makeSearchSource(
|
||||
searchFilters: VectorLayerRequestMeta,
|
||||
searchFilters: VectorSourceRequestMeta,
|
||||
limit: number,
|
||||
initialSearchContext?: object
|
||||
): Promise<SearchSource>;
|
||||
|
|
|
@ -9,15 +9,28 @@ import { ILayer } from '../layer';
|
|||
|
||||
export interface ISource {
|
||||
createDefaultLayer(): ILayer;
|
||||
getDisplayName(): Promise<string>;
|
||||
destroy(): void;
|
||||
getDisplayName(): Promise<string>;
|
||||
getInspectorAdapters(): object;
|
||||
isFieldAware(): boolean;
|
||||
isFilterByMapBounds(): boolean;
|
||||
isGeoGridPrecisionAware(): boolean;
|
||||
isQueryAware(): boolean;
|
||||
isRefreshTimerAware(): Promise<boolean>;
|
||||
isTimeAware(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export class AbstractSource implements ISource {
|
||||
constructor(sourceDescriptor: AbstractSourceDescriptor, inspectorAdapters: object);
|
||||
|
||||
destroy(): void;
|
||||
createDefaultLayer(): ILayer;
|
||||
getDisplayName(): Promise<string>;
|
||||
destroy(): void;
|
||||
getInspectorAdapters(): object;
|
||||
isFieldAware(): boolean;
|
||||
isFilterByMapBounds(): boolean;
|
||||
isGeoGridPrecisionAware(): boolean;
|
||||
isQueryAware(): boolean;
|
||||
isRefreshTimerAware(): Promise<boolean>;
|
||||
isTimeAware(): Promise<boolean>;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
/* eslint-disable @typescript-eslint/consistent-type-definitions */
|
||||
|
||||
import { FeatureCollection } from 'geojson';
|
||||
import { AbstractSource, ISource } from './source';
|
||||
import { IField } from '../fields/field';
|
||||
import { ESSearchSourceResponseMeta } from '../../../common/data_request_descriptor_types';
|
||||
|
@ -12,7 +13,7 @@ import { ESSearchSourceResponseMeta } from '../../../common/data_request_descrip
|
|||
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
|
||||
|
||||
export type GeoJsonWithMeta = {
|
||||
data: unknown; // geojson feature collection
|
||||
data: FeatureCollection;
|
||||
meta?: GeoJsonFetchMeta;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { TileLayer } from './tile_layer';
|
||||
import { EMS_XYZ } from '../../common/constants';
|
||||
import { XYZTMSSourceDescriptor } from '../../common/descriptor_types';
|
||||
import { ITMSSource } from './sources/tms_source';
|
||||
import { ITMSSource, AbstractTMSSource } from './sources/tms_source';
|
||||
import { ILayer } from './layer';
|
||||
|
||||
const sourceDescriptor: XYZTMSSourceDescriptor = {
|
||||
|
@ -16,9 +16,10 @@ const sourceDescriptor: XYZTMSSourceDescriptor = {
|
|||
id: 'foobar',
|
||||
};
|
||||
|
||||
class MockTileSource implements ITMSSource {
|
||||
class MockTileSource extends AbstractTMSSource implements ITMSSource {
|
||||
private readonly _descriptor: XYZTMSSourceDescriptor;
|
||||
constructor(descriptor: XYZTMSSourceDescriptor) {
|
||||
super(descriptor, {});
|
||||
this._descriptor = descriptor;
|
||||
}
|
||||
createDefaultLayer(): ILayer {
|
||||
|
@ -32,14 +33,6 @@ class MockTileSource implements ITMSSource {
|
|||
async getUrlTemplate(): Promise<string> {
|
||||
return 'template/{x}/{y}/{z}.png';
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
// no-op
|
||||
}
|
||||
|
||||
getInspectorAdapters(): object {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
describe('TileLayer', () => {
|
||||
|
|
|
@ -6,19 +6,25 @@
|
|||
|
||||
import { assignFeatureIds } from './assign_feature_ids';
|
||||
import { FEATURE_ID_PROPERTY_NAME } from '../../../common/constants';
|
||||
import { FeatureCollection, Feature, Point } from 'geojson';
|
||||
|
||||
const featureId = 'myFeature1';
|
||||
|
||||
const geometry: Point = {
|
||||
type: 'Point',
|
||||
coordinates: [0, 0],
|
||||
};
|
||||
|
||||
const defaultFeature: Feature = {
|
||||
type: 'Feature',
|
||||
geometry,
|
||||
properties: {},
|
||||
};
|
||||
|
||||
test('should provide unique id when feature.id is not provided', () => {
|
||||
const featureCollection = {
|
||||
features: [
|
||||
{
|
||||
properties: {},
|
||||
},
|
||||
{
|
||||
properties: {},
|
||||
},
|
||||
],
|
||||
const featureCollection: FeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [{ ...defaultFeature }, { ...defaultFeature }],
|
||||
};
|
||||
|
||||
const updatedFeatureCollection = assignFeatureIds(featureCollection);
|
||||
|
@ -26,16 +32,18 @@ test('should provide unique id when feature.id is not provided', () => {
|
|||
const feature2 = updatedFeatureCollection.features[1];
|
||||
expect(typeof feature1.id).toBe('number');
|
||||
expect(typeof feature2.id).toBe('number');
|
||||
// @ts-ignore
|
||||
expect(feature1.id).toBe(feature1.properties[FEATURE_ID_PROPERTY_NAME]);
|
||||
expect(feature1.id).not.toBe(feature2.id);
|
||||
});
|
||||
|
||||
test('should preserve feature id when provided', () => {
|
||||
const featureCollection = {
|
||||
const featureCollection: FeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
...defaultFeature,
|
||||
id: featureId,
|
||||
properties: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -43,16 +51,19 @@ test('should preserve feature id when provided', () => {
|
|||
const updatedFeatureCollection = assignFeatureIds(featureCollection);
|
||||
const feature1 = updatedFeatureCollection.features[0];
|
||||
expect(typeof feature1.id).toBe('number');
|
||||
// @ts-ignore
|
||||
expect(feature1.id).not.toBe(feature1.properties[FEATURE_ID_PROPERTY_NAME]);
|
||||
// @ts-ignore
|
||||
expect(feature1.properties[FEATURE_ID_PROPERTY_NAME]).toBe(featureId);
|
||||
});
|
||||
|
||||
test('should preserve feature id for falsy value', () => {
|
||||
const featureCollection = {
|
||||
const featureCollection: FeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
...defaultFeature,
|
||||
id: 0,
|
||||
properties: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -60,15 +71,19 @@ test('should preserve feature id for falsy value', () => {
|
|||
const updatedFeatureCollection = assignFeatureIds(featureCollection);
|
||||
const feature1 = updatedFeatureCollection.features[0];
|
||||
expect(typeof feature1.id).toBe('number');
|
||||
// @ts-ignore
|
||||
expect(feature1.id).not.toBe(feature1.properties[FEATURE_ID_PROPERTY_NAME]);
|
||||
// @ts-ignore
|
||||
expect(feature1.properties[FEATURE_ID_PROPERTY_NAME]).toBe(0);
|
||||
});
|
||||
|
||||
test('should not modify original feature properties', () => {
|
||||
const featureProperties = {};
|
||||
const featureCollection = {
|
||||
const featureCollection: FeatureCollection = {
|
||||
type: 'FeatureCollection',
|
||||
features: [
|
||||
{
|
||||
...defaultFeature,
|
||||
id: featureId,
|
||||
properties: featureProperties,
|
||||
},
|
||||
|
@ -77,6 +92,7 @@ test('should not modify original feature properties', () => {
|
|||
|
||||
const updatedFeatureCollection = assignFeatureIds(featureCollection);
|
||||
const feature1 = updatedFeatureCollection.features[0];
|
||||
// @ts-ignore
|
||||
expect(feature1.properties[FEATURE_ID_PROPERTY_NAME]).toBe(featureId);
|
||||
expect(featureProperties).not.toHaveProperty(FEATURE_ID_PROPERTY_NAME);
|
||||
});
|
|
@ -5,17 +5,18 @@
|
|||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { FeatureCollection, Feature } from 'geojson';
|
||||
import { FEATURE_ID_PROPERTY_NAME } from '../../../common/constants';
|
||||
|
||||
let idCounter = 0;
|
||||
|
||||
function generateNumericalId() {
|
||||
function generateNumericalId(): number {
|
||||
const newId = idCounter < Number.MAX_SAFE_INTEGER ? idCounter : 0;
|
||||
idCounter = newId + 1;
|
||||
return newId;
|
||||
}
|
||||
|
||||
export function assignFeatureIds(featureCollection) {
|
||||
export function assignFeatureIds(featureCollection: FeatureCollection): FeatureCollection {
|
||||
// wrt https://github.com/elastic/kibana/issues/39317
|
||||
// In constrained resource environments, mapbox-gl may throw a stackoverflow error due to hitting the browser's recursion limit. This crashes Kibana.
|
||||
// This error is thrown in mapbox-gl's quicksort implementation, when it is sorting all the features by id.
|
||||
|
@ -32,7 +33,7 @@ export function assignFeatureIds(featureCollection) {
|
|||
}
|
||||
|
||||
const randomizedIds = _.shuffle(ids);
|
||||
const features = [];
|
||||
const features: Feature[] = [];
|
||||
for (let i = 0; i < featureCollection.features.length; i++) {
|
||||
const numericId = randomizedIds[i];
|
||||
const feature = featureCollection.features[i];
|
|
@ -4,14 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import _ from 'lodash';
|
||||
// @ts-ignore
|
||||
import turf from 'turf';
|
||||
import turfBooleanContains from '@turf/boolean-contains';
|
||||
import { isRefreshOnlyQuery } from './is_refresh_only_query';
|
||||
import { ISource } from '../sources/source';
|
||||
import { DataMeta } from '../../../common/data_request_descriptor_types';
|
||||
import { DataRequest } from './data_request';
|
||||
|
||||
const SOURCE_UPDATE_REQUIRED = true;
|
||||
const NO_SOURCE_UPDATE_REQUIRED = false;
|
||||
|
||||
export function updateDueToExtent(source, prevMeta = {}, nextMeta = {}) {
|
||||
export function updateDueToExtent(
|
||||
source: ISource,
|
||||
prevMeta: DataMeta = {},
|
||||
nextMeta: DataMeta = {}
|
||||
) {
|
||||
const extentAware = source.isFilterByMapBounds();
|
||||
if (!extentAware) {
|
||||
return NO_SOURCE_UPDATE_REQUIRED;
|
||||
|
@ -20,7 +28,7 @@ export function updateDueToExtent(source, prevMeta = {}, nextMeta = {}) {
|
|||
const { buffer: previousBuffer } = prevMeta;
|
||||
const { buffer: newBuffer } = nextMeta;
|
||||
|
||||
if (!previousBuffer) {
|
||||
if (!previousBuffer || !previousBuffer || !newBuffer) {
|
||||
return SOURCE_UPDATE_REQUIRED;
|
||||
}
|
||||
|
||||
|
@ -51,7 +59,15 @@ export function updateDueToExtent(source, prevMeta = {}, nextMeta = {}) {
|
|||
: SOURCE_UPDATE_REQUIRED;
|
||||
}
|
||||
|
||||
export async function canSkipSourceUpdate({ source, prevDataRequest, nextMeta }) {
|
||||
export async function canSkipSourceUpdate({
|
||||
source,
|
||||
prevDataRequest,
|
||||
nextMeta,
|
||||
}: {
|
||||
source: ISource;
|
||||
prevDataRequest: DataRequest | undefined;
|
||||
nextMeta: DataMeta;
|
||||
}): Promise<boolean> {
|
||||
const timeAware = await source.isTimeAware();
|
||||
const refreshTimerAware = await source.isRefreshTimerAware();
|
||||
const extentAware = source.isFilterByMapBounds();
|
||||
|
@ -67,7 +83,7 @@ export async function canSkipSourceUpdate({ source, prevDataRequest, nextMeta })
|
|||
!isQueryAware &&
|
||||
!isGeoGridPrecisionAware
|
||||
) {
|
||||
return prevDataRequest && prevDataRequest.hasDataOrRequestInProgress();
|
||||
return !!prevDataRequest && prevDataRequest.hasDataOrRequestInProgress();
|
||||
}
|
||||
|
||||
if (!prevDataRequest) {
|
||||
|
@ -136,7 +152,13 @@ export async function canSkipSourceUpdate({ source, prevDataRequest, nextMeta })
|
|||
);
|
||||
}
|
||||
|
||||
export function canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }) {
|
||||
export function canSkipStyleMetaUpdate({
|
||||
prevDataRequest,
|
||||
nextMeta,
|
||||
}: {
|
||||
prevDataRequest: DataRequest | undefined;
|
||||
nextMeta: DataMeta;
|
||||
}): boolean {
|
||||
if (!prevDataRequest) {
|
||||
return false;
|
||||
}
|
||||
|
@ -159,7 +181,13 @@ export function canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }) {
|
|||
);
|
||||
}
|
||||
|
||||
export function canSkipFormattersUpdate({ prevDataRequest, nextMeta }) {
|
||||
export function canSkipFormattersUpdate({
|
||||
prevDataRequest,
|
||||
nextMeta,
|
||||
}: {
|
||||
prevDataRequest: DataRequest | undefined;
|
||||
nextMeta: DataMeta;
|
||||
}): boolean {
|
||||
if (!prevDataRequest) {
|
||||
return false;
|
||||
}
|
|
@ -4,9 +4,14 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Query } from '../../../common/map_descriptor';
|
||||
|
||||
// Refresh only query is query where timestamps are different but query is the same.
|
||||
// Triggered by clicking "Refresh" button in QueryBar
|
||||
export function isRefreshOnlyQuery(prevQuery, newQuery) {
|
||||
export function isRefreshOnlyQuery(
|
||||
prevQuery: Query | undefined,
|
||||
newQuery: Query | undefined
|
||||
): boolean {
|
||||
if (!prevQuery || !newQuery) {
|
||||
return false;
|
||||
}
|
|
@ -34,14 +34,14 @@ const POINT_MB_FILTER = [
|
|||
|
||||
const VISIBLE_POINT_MB_FILTER = [...VISIBILITY_FILTER_CLAUSE, POINT_MB_FILTER];
|
||||
|
||||
export function getFillFilterExpression(hasJoins) {
|
||||
export function getFillFilterExpression(hasJoins: boolean): unknown[] {
|
||||
return hasJoins ? VISIBLE_CLOSED_SHAPE_MB_FILTER : CLOSED_SHAPE_MB_FILTER;
|
||||
}
|
||||
|
||||
export function getLineFilterExpression(hasJoins) {
|
||||
export function getLineFilterExpression(hasJoins: boolean): unknown[] {
|
||||
return hasJoins ? VISIBLE_ALL_SHAPE_MB_FILTER : ALL_SHAPE_MB_FILTER;
|
||||
}
|
||||
|
||||
export function getPointFilterExpression(hasJoins) {
|
||||
export function getPointFilterExpression(hasJoins: boolean): unknown[] {
|
||||
return hasJoins ? VISIBLE_POINT_MB_FILTER : POINT_MB_FILTER;
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import { AbstractLayer } from './layer';
|
||||
import { IVectorSource } from './sources/vector_source';
|
||||
import { VectorLayerDescriptor } from '../../common/descriptor_types';
|
||||
import { MapFilters, VectorLayerRequestMeta } from '../../common/data_request_descriptor_types';
|
||||
import { MapFilters, VectorSourceRequestMeta } from '../../common/data_request_descriptor_types';
|
||||
import { ILayer } from './layer';
|
||||
import { IJoin } from './joins/join';
|
||||
import { IVectorStyle } from './styles/vector/vector_style';
|
||||
|
@ -45,6 +45,6 @@ export class VectorLayer extends AbstractLayer implements IVectorLayer {
|
|||
dataFilters: MapFilters,
|
||||
source: IVectorSource,
|
||||
style: IVectorStyle
|
||||
): VectorLayerRequestMeta;
|
||||
): VectorSourceRequestMeta;
|
||||
_syncData(syncContext: SyncContext, source: IVectorSource, style: IVectorStyle): Promise<void>;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
"@types/elasticsearch": "^5.0.33",
|
||||
"@types/fancy-log": "^1.3.1",
|
||||
"@types/file-saver": "^2.0.0",
|
||||
"@types/geojson": "7946.0.7",
|
||||
"@types/getos": "^3.0.0",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
|
|
|
@ -5175,7 +5175,7 @@
|
|||
dependencies:
|
||||
"@types/jquery" "*"
|
||||
|
||||
"@types/geojson@*":
|
||||
"@types/geojson@*", "@types/geojson@7946.0.7":
|
||||
version "7946.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.7.tgz#c8fa532b60a0042219cdf173ca21a975ef0666ad"
|
||||
integrity sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue