diff --git a/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts b/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts index 3281fb5892ea..a0102a4249a5 100644 --- a/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts +++ b/x-pack/legacy/plugins/maps/common/data_request_descriptor_types.d.ts @@ -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 & Partial; +export type DataMeta = Partial & + Partial & + Partial; export type DataRequestDescriptor = { dataId: string; diff --git a/x-pack/legacy/plugins/maps/common/map_descriptor.ts b/x-pack/legacy/plugins/maps/common/map_descriptor.ts new file mode 100644 index 000000000000..570398e37c5d --- /dev/null +++ b/x-pack/legacy/plugins/maps/common/map_descriptor.ts @@ -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; +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts b/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts index 22990538bd5d..8c54720987e4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts +++ b/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts @@ -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'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts index 963a30c7413e..b565cb9108ae 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts @@ -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; makeSearchSource( - searchFilters: VectorLayerRequestMeta, + searchFilters: VectorSourceRequestMeta, limit: number, initialSearchContext?: object ): Promise; @@ -29,7 +29,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource getGeoFieldName(): string; getMaxResultWindow(): Promise; makeSearchSource( - searchFilters: VectorLayerRequestMeta, + searchFilters: VectorSourceRequestMeta, limit: number, initialSearchContext?: object ): Promise; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts index 2ca18e47a4bf..e1706ad7b7d7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts @@ -9,15 +9,28 @@ import { ILayer } from '../layer'; export interface ISource { createDefaultLayer(): ILayer; - getDisplayName(): Promise; destroy(): void; + getDisplayName(): Promise; getInspectorAdapters(): object; + isFieldAware(): boolean; + isFilterByMapBounds(): boolean; + isGeoGridPrecisionAware(): boolean; + isQueryAware(): boolean; + isRefreshTimerAware(): Promise; + isTimeAware(): Promise; } export class AbstractSource implements ISource { constructor(sourceDescriptor: AbstractSourceDescriptor, inspectorAdapters: object); + + destroy(): void; createDefaultLayer(): ILayer; getDisplayName(): Promise; - destroy(): void; getInspectorAdapters(): object; + isFieldAware(): boolean; + isFilterByMapBounds(): boolean; + isGeoGridPrecisionAware(): boolean; + isQueryAware(): boolean; + isRefreshTimerAware(): Promise; + isTimeAware(): Promise; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts index 14fc23751ac1..fd585e100924 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts @@ -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; }; diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts b/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts index 8ce38a128ebc..1cb99dcbc1a7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts +++ b/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts @@ -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 { return 'template/{x}/{y}/{z}.png'; } - - destroy(): void { - // no-op - } - - getInspectorAdapters(): object { - return {}; - } } describe('TileLayer', () => { diff --git a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.js b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.ts similarity index 74% rename from x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.js rename to x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.ts index cc8dff14ec4f..fb07a523e1e0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.ts @@ -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); }); diff --git a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.ts similarity index 90% rename from x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js rename to x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.ts index a943b0b22a18..e5c170a80317 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.ts @@ -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]; diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.ts similarity index 84% rename from x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js rename to x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.ts index 7abfee1b184f..7b75bb0f21b7 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.ts @@ -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 { 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; } diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts similarity index 78% rename from x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js rename to x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts index f3dc08a7a7a5..48b1340207fd 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts @@ -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; } diff --git a/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js b/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.ts similarity index 87% rename from x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js rename to x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.ts index 36841dc727dd..8da6fa2318de 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.js +++ b/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.ts @@ -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; } diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts b/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts index 77e8ab768cd0..390374f761fc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts +++ b/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts @@ -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; } diff --git a/x-pack/package.json b/x-pack/package.json index 254240b461f6..cbf1789310b5 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index 060716fe1803..b6cd8e528e52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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==