Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-78988
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Oct 3, 2020
2 parents 2a4ef87 + 42fc027 commit 45605ce
Show file tree
Hide file tree
Showing 19 changed files with 251 additions and 75 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/maps/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
import { FeatureCollection } from 'geojson';

export const EMS_APP_NAME = 'kibana';
export const EMS_CATALOGUE_PATH = 'ems/catalogue';

Expand Down
51 changes: 51 additions & 0 deletions x-pack/plugins/maps/public/actions/layer_actions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { addLayer } from './layer_actions';
import { LayerDescriptor } from '../../common/descriptor_types';
import { LICENSED_FEATURES } from '../licensed_features';

const getStoreMock = jest.fn();
const dispatchMock = jest.fn();

describe('layer_actions', () => {
afterEach(() => {
jest.resetAllMocks();
});

describe('addLayer', () => {
const notifyLicensedFeatureUsageMock = jest.fn();

beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../licensed_features').notifyLicensedFeatureUsage = (feature: LICENSED_FEATURES) => {
notifyLicensedFeatureUsageMock(feature);
};

// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../selectors/map_selectors').getMapReady = () => {
return true;
};

// eslint-disable-next-line @typescript-eslint/no-var-requires
require('../selectors/map_selectors').createLayerInstance = () => {
return {
getLicensedFeatures() {
return [LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE];
},
};
};
});

it('should register feature-use', async () => {
const action = addLayer(({} as unknown) as LayerDescriptor);
await action(dispatchMock, getStoreMock);
expect(notifyLicensedFeatureUsageMock).toHaveBeenCalledWith(
LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE
);
});
});
});
8 changes: 7 additions & 1 deletion x-pack/plugins/maps/public/actions/layer_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getSelectedLayerId,
getMapReady,
getMapColors,
createLayerInstance,
} from '../selectors/map_selectors';
import { FLYOUT_STATE } from '../reducers/ui';
import { cancelRequest } from '../reducers/non_serializable_instances';
Expand Down Expand Up @@ -42,6 +43,7 @@ import { ILayer } from '../classes/layers/layer';
import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer';
import { LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants';
import { IVectorStyle } from '../classes/styles/vector/vector_style';
import { notifyLicensedFeatureUsage } from '../licensed_features';

export function trackCurrentLayerState(layerId: string) {
return {
Expand Down Expand Up @@ -108,7 +110,7 @@ export function cloneLayer(layerId: string) {
}

export function addLayer(layerDescriptor: LayerDescriptor) {
return (dispatch: Dispatch, getState: () => MapStoreState) => {
return async (dispatch: Dispatch, getState: () => MapStoreState) => {
const isMapReady = getMapReady(getState());
if (!isMapReady) {
dispatch({
Expand All @@ -123,6 +125,10 @@ export function addLayer(layerDescriptor: LayerDescriptor) {
layer: layerDescriptor,
});
dispatch<any>(syncDataForLayerId(layerDescriptor.id));

const layer = createLayerInstance(layerDescriptor);
const features = await layer.getLicensedFeatures();
features.forEach(notifyLicensedFeatureUsage);
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
VectorLayerDescriptor,
} from '../../../../common/descriptor_types';
import { IVectorSource } from '../../sources/vector_source';
import { LICENSED_FEATURES } from '../../../licensed_features';

const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID';

Expand Down Expand Up @@ -327,4 +328,11 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {

super._syncData(syncContext, activeSource, activeStyle);
}

async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> {
return [
...(await this._clusterSource.getLicensedFeatures()),
...(await this._documentSource.getLicensedFeatures()),
];
}
}
6 changes: 6 additions & 0 deletions x-pack/plugins/maps/public/classes/layers/layer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { Attribution, ImmutableSourceProperty, ISource, SourceEditorArgs } from
import { DataRequestContext } from '../../actions';
import { IStyle } from '../styles/style';
import { getJoinAggKey } from '../../../common/get_agg_key';
import { LICENSED_FEATURES } from '../../licensed_features';

export interface ILayer {
getBounds(dataRequestContext: DataRequestContext): Promise<MapExtent | null>;
Expand Down Expand Up @@ -91,6 +92,7 @@ export interface ILayer {
showJoinEditor(): boolean;
getJoinsDisabledReason(): string | null;
isFittable(): Promise<boolean>;
getLicensedFeatures(): Promise<LICENSED_FEATURES[]>;
}
export type Footnote = {
icon: ReactElement<any>;
Expand Down Expand Up @@ -538,4 +540,8 @@ export class AbstractLayer implements ILayer {
supportsLabelsOnTop(): boolean {
return false;
}

async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> {
return [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1090,4 +1090,8 @@ export class VectorLayer extends AbstractLayer {
});
return targetFeature ? targetFeature : null;
}

async getLicensedFeatures() {
return await this._source.getLicensedFeatures();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import {
MVT_GETGRIDTILE_API_PATH,
GEOTILE_GRID_AGG_NAME,
GEOCENTROID_AGG_NAME,
ES_GEO_FIELD_TYPE,
} from '../../../../common/constants';
import { i18n } from '@kbn/i18n';
import { getDataSourceLabel } from '../../../../common/i18n_getters';
import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source';
import { DataRequestAbortError } from '../../util/data_request';
import { registerSource } from '../source_registry';
import { LICENSED_FEATURES } from '../../../licensed_features';

import rison from 'rison-node';
import { getHttp } from '../../../kibana_services';
Expand Down Expand Up @@ -399,6 +401,13 @@ export class ESGeoGridSource extends AbstractESAggSource {

return [VECTOR_SHAPE_TYPE.POINT];
}

async getLicensedFeatures() {
const geoField = await this._getGeoField();
return geoField.type === ES_GEO_FIELD_TYPE.GEO_SHAPE
? [LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE]
: [];
}
}

registerSource({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { MapExtent, VectorSourceRequestMeta } from '../../../../common/descriptor_types';

jest.mock('../../../kibana_services');

import { getIndexPatternService, getSearchService, getHttp } from '../../../kibana_services';
import { getHttp, getIndexPatternService, getSearchService } from '../../../kibana_services';
import { ESGeoGridSource } from './es_geo_grid_source';
import {
ES_GEO_FIELD_TYPE,
Expand All @@ -16,6 +13,9 @@ import {
SOURCE_TYPES,
} from '../../../../common/constants';
import { SearchSource } from 'src/plugins/data/public';
import { LICENSED_FEATURES } from '../../../licensed_features';

jest.mock('../../../kibana_services');

export class MockSearchSource {
setField = jest.fn();
Expand All @@ -27,14 +27,16 @@ export class MockSearchSource {

describe('ESGeoGridSource', () => {
const geoFieldName = 'bar';

let esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_POINT;
const mockIndexPatternService = {
get() {
return {
fields: {
getByName() {
return {
name: geoFieldName,
type: ES_GEO_FIELD_TYPE.GEO_POINT,
type: esGeoFieldType,
};
},
},
Expand Down Expand Up @@ -127,6 +129,11 @@ describe('ESGeoGridSource', () => {
});
});

afterEach(() => {
esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_POINT;
jest.resetAllMocks();
});

const extent: MapExtent = {
minLon: -160,
minLat: -80,
Expand Down Expand Up @@ -271,4 +278,17 @@ describe('ESGeoGridSource', () => {
);
});
});

describe('Gold+ usage', () => {
it('Should have none for points', async () => {
expect(await geogridSource.getLicensedFeatures()).toEqual([]);
});

it('Should have shape-aggs for geo_shape', async () => {
esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_SHAPE;
expect(await geogridSource.getLicensedFeatures()).toEqual([
LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE,
]);
});
});
});
6 changes: 6 additions & 0 deletions x-pack/plugins/maps/public/classes/sources/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IField } from '../fields/field';
import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants';
import { AbstractSourceDescriptor } from '../../../common/descriptor_types';
import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view';
import { LICENSED_FEATURES } from '../../licensed_features';

export type SourceEditorArgs = {
onChange: (...args: OnSourceChangeArgs[]) => void;
Expand Down Expand Up @@ -66,6 +67,7 @@ export interface ISource {
getValueSuggestions(field: IField, query: string): Promise<string[]>;
getMinZoom(): number;
getMaxZoom(): number;
getLicensedFeatures(): Promise<LICENSED_FEATURES[]>;
}

export class AbstractSource implements ISource {
Expand Down Expand Up @@ -188,4 +190,8 @@ export class AbstractSource implements ISource {
getMaxZoom() {
return MAX_ZOOM;
}

async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> {
return [];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

import _ from 'lodash';
import React from 'react';
import { Feature } from 'geojson';
import { Feature, FeatureCollection } from 'geojson';
import { FeatureIdentifier, Map as MbMap } from 'mapbox-gl';
import { AbstractStyleProperty, IStyleProperty } from './style_property';
import { DEFAULT_SIGMA } from '../vector_style_defaults';
import {
Expand Down Expand Up @@ -44,20 +45,14 @@ export interface IDynamicStyleProperty<T> extends IStyleProperty<T> {
isOrdinal(): boolean;
supportsFieldMeta(): boolean;
getFieldMetaRequest(): Promise<unknown>;
supportsMbFeatureState(): boolean;
getMbLookupFunction(): MB_LOOKUP_FUNCTION;
pluckOrdinalStyleMetaFromFeatures(features: Feature[]): RangeFieldMeta | null;
pluckCategoricalStyleMetaFromFeatures(features: Feature[]): CategoryFieldMeta | null;
getValueSuggestions(query: string): Promise<string[]>;

// Returns the name that should be used for accessing the data from the mb-style rule
// Depending on
// - whether the field is used for labeling, icon-orientation, or other properties (color, size, ...), `feature-state` and or `get` is used
// - whether the field was run through a field-formatter, a new dynamic field is created with the formatted-value
// The combination of both will inform what field-name (e.g. the "raw" field name from the properties, the "computed field-name" for an on-the-fly created property (e.g. for feature-state or field-formatting).
// todo: There is an existing limitation to .mvt backed sources, where the field-formatters are not applied. Here, the raw-data needs to be accessed.
getMbPropertyName(): string;
getMbPropertyValue(value: RawValue): RawValue;
enrichGeoJsonAndMbFeatureState(
featureCollection: FeatureCollection,
mbMap: MbMap,
mbSourceId: string
): boolean;
}

export class DynamicStyleProperty<T>
Expand Down Expand Up @@ -356,6 +351,12 @@ export class DynamicStyleProperty<T>
);
}

// Returns the name that should be used for accessing the data from the mb-style rule
// Depending on
// - whether the field is used for labeling, icon-orientation, or other properties (color, size, ...), `feature-state` and or `get` is used
// - whether the field was run through a field-formatter, a new dynamic field is created with the formatted-value
// The combination of both will inform what field-name (e.g. the "raw" field name from the properties, the "computed field-name" for an on-the-fly created property (e.g. for feature-state or field-formatting).
// todo: There is an existing limitation to .mvt backed sources, where the field-formatters are not applied. Here, the raw-data needs to be accessed.
getMbPropertyName() {
if (!this._field) {
return '';
Expand Down Expand Up @@ -385,6 +386,35 @@ export class DynamicStyleProperty<T>
// Calling `isOrdinal` would be equivalent.
return this.supportsMbFeatureState() ? getNumericalMbFeatureStateValue(rawValue) : rawValue;
}

enrichGeoJsonAndMbFeatureState(
featureCollection: FeatureCollection,
mbMap: MbMap,
mbSourceId: string
): boolean {
const supportsFeatureState = this.supportsMbFeatureState();
const featureIdentifier: FeatureIdentifier = {
source: mbSourceId,
id: undefined,
};
const featureState: Record<string, RawValue> = {};
const targetMbName = this.getMbPropertyName();
for (let i = 0; i < featureCollection.features.length; i++) {
const feature = featureCollection.features[i];
const rawValue = feature.properties ? feature.properties[this.getFieldName()] : undefined;
const targetMbValue = this.getMbPropertyValue(rawValue);
if (supportsFeatureState) {
featureState[targetMbName] = targetMbValue; // the same value will be potentially overridden multiple times, if the name remains identical
featureIdentifier.id = feature.id;
mbMap.setFeatureState(featureIdentifier, featureState);
} else {
if (feature.properties) {
feature.properties[targetMbName] = targetMbValue;
}
}
}
return supportsFeatureState;
}
}

export function getNumericalMbFeatureStateValue(value: RawValue) {
Expand Down
Loading

0 comments on commit 45605ce

Please sign in to comment.