From 6919f97ce09276de4f49cfff9e66d81405ad63c1 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 8 Jun 2020 16:41:16 -0600 Subject: [PATCH 1/9] [Maps] surface geo_shape clustering gold feature --- x-pack/plugins/maps/common/constants.ts | 2 + .../create_source_editor.js | 49 ++++++-- .../es_search_source/create_source_editor.js | 16 +-- .../public/components/single_field_select.js | 68 ---------- .../public/components/single_field_select.tsx | 117 ++++++++++++++++++ .../plugins/maps/public/index_pattern_util.js | 8 +- 6 files changed, 169 insertions(+), 91 deletions(-) delete mode 100644 x-pack/plugins/maps/public/components/single_field_select.js create mode 100644 x-pack/plugins/maps/public/components/single_field_select.tsx diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index d357f11f5e3e1e..0961b5ed5f8ebc 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -99,6 +99,8 @@ export enum ES_GEO_FIELD_TYPE { GEO_SHAPE = 'geo_shape', } +export const ES_GEO_FIELD_TYPES = [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]; + export enum ES_SPATIAL_RELATIONS { INTERSECTS = 'INTERSECTS', DISJOINT = 'DISJOINT', diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js index 91dcb057dd8378..f577b266013802 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js @@ -8,15 +8,46 @@ import _ from 'lodash'; import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; +import { ES_GEO_FIELD_TYPES } from '../../../../common/constants'; import { SingleFieldSelect } from '../../../components/single_field_select'; -import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services'; +import { + getIndexPatternService, + getIndexPatternSelectComponent, + getIsGoldPlus, +} from '../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { getAggregatableGeoFieldTypes, getFieldsWithGeoTileAgg } from '../../../index_pattern_util'; +import { + getFieldsWithGeoTileAgg, + getGeoFields, + supportsGeoTileAgg, +} from '../../../index_pattern_util'; import { RenderAsSelect } from './render_as_select'; +function doesNotSupportGeoTileAgg(field) { + return !supportsGeoTileAgg(field); +} + +function getGeoTileAggNotSupportedReason(field) { + if (!field.aggregatable) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.docValues', { + defaultMessage: + 'Doc values not enabled for {field.name}. Turn on doc_values in elasticsearch index mapping to enable.', + values: { name: field.name }, + }); + } + + if (!getIsGoldPlus()) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.license', { + defaultMessage: 'Clustering geo_shape fields requires Gold license or higher.', + }); + } + + return null; +} + export class CreateSourceEditor extends Component { static propTypes = { onSourceConfigChange: PropTypes.func.isRequired, @@ -87,9 +118,9 @@ export class CreateSourceEditor extends Component { }); //make default selection - const geoFields = getFieldsWithGeoTileAgg(indexPattern.fields); - if (geoFields[0]) { - this._onGeoFieldSelect(geoFields[0].name); + const geoFieldsWithGeoTileAgg = getFieldsWithGeoTileAgg(indexPattern.fields); + if (geoFieldsWithGeoTileAgg[0]) { + this._onGeoFieldSelect(geoFieldsWithGeoTileAgg[0].name); } }, 300); @@ -141,10 +172,10 @@ export class CreateSourceEditor extends Component { value={this.state.geoField} onChange={this._onGeoFieldSelect} fields={ - this.state.indexPattern - ? getFieldsWithGeoTileAgg(this.state.indexPattern.fields) - : undefined + this.state.indexPattern ? getGeoFields(this.state.indexPattern.fields) : undefined } + isFieldDisabled={doesNotSupportGeoTileAgg} + getFieldDisabledReason={getGeoTileAggNotSupportedReason} /> ); @@ -176,7 +207,7 @@ export class CreateSourceEditor extends Component { placeholder={i18n.translate('xpack.maps.source.esGeoGrid.indexPatternPlaceholder', { defaultMessage: 'Select index pattern', })} - fieldTypes={getAggregatableGeoFieldTypes()} + fieldTypes={ES_GEO_FIELD_TYPES} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js index 2f90b9b50d3d93..c0bf89f8586c65 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/create_source_editor.js @@ -13,20 +13,10 @@ import { SingleFieldSelect } from '../../../components/single_field_select'; import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; -import { ES_GEO_FIELD_TYPE, SCALING_TYPES } from '../../../../common/constants'; +import { ES_GEO_FIELD_TYPES, SCALING_TYPES } from '../../../../common/constants'; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants'; -import { indexPatterns } from '../../../../../../../src/plugins/data/public'; import { ScalingForm } from './scaling_form'; -import { getTermsFields, supportsGeoTileAgg } from '../../../index_pattern_util'; - -function getGeoFields(fields) { - return fields.filter((field) => { - return ( - !indexPatterns.isNestedField(field) && - [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE].includes(field.type) - ); - }); -} +import { getGeoFields, getTermsFields, supportsGeoTileAgg } from '../../../index_pattern_util'; function doesGeoFieldSupportGeoTileAgg(indexPattern, geoFieldName) { return indexPattern ? supportsGeoTileAgg(indexPattern.fields.getByName(geoFieldName)) : false; @@ -260,7 +250,7 @@ export class CreateSourceEditor extends Component { defaultMessage: 'Select index pattern', } )} - fieldTypes={[ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]} + fieldTypes={ES_GEO_FIELD_TYPES} onNoIndexPatterns={this._onNoIndexPatterns} /> diff --git a/x-pack/plugins/maps/public/components/single_field_select.js b/x-pack/plugins/maps/public/components/single_field_select.js deleted file mode 100644 index a4db361da9c623..00000000000000 --- a/x-pack/plugins/maps/public/components/single_field_select.js +++ /dev/null @@ -1,68 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; - -function fieldsToOptions(fields) { - if (!fields) { - return []; - } - - return fields - .map((field) => { - return { - value: field, - label: 'label' in field ? field.label : field.name, - }; - }) - .sort((a, b) => { - return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); - }); -} - -function renderOption(option, searchValue, contentClassName) { - return ( - - - - - - {option.label} - - - ); -} - -export function SingleFieldSelect({ fields, onChange, value, placeholder, ...rest }) { - const onSelection = (selectedOptions) => { - onChange(_.get(selectedOptions, '0.value.name')); - }; - - return ( - - ); -} - -SingleFieldSelect.propTypes = { - placeholder: PropTypes.string, - fields: PropTypes.array, - onChange: PropTypes.func.isRequired, - value: PropTypes.string, // fieldName -}; diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx new file mode 100644 index 00000000000000..d0a6f45da5e1ea --- /dev/null +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -0,0 +1,117 @@ +/* + * 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 _ from 'lodash'; +import React from 'react'; + +import { + EuiComboBox, + EuiComboBoxProps, + EuiComboBoxOptionOption, + EuiHighlight, + EuiFlexGroup, + EuiFlexItem, + EuiToolTip, +} from '@elastic/eui'; +import { IFieldType } from 'src/plugins/data/public'; +import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; + +function fieldsToOptions( + fields: IFieldType[], + isFieldDisabled?: (field: IField) => boolean +): Array> { + if (!fields) { + return []; + } + + return fields + .map((field) => { + const option: EuiComboBoxOptionOption = { + value: field, + label: 'label' in field ? field.label : field.name, + }; + if (isFieldDisabled && isFieldDisabled(field)) { + option.disabled = true; + } + return option; + }) + .sort((a, b) => { + return a.label.toLowerCase().localeCompare(b.label.toLowerCase()); + }); +} + +type Props = Omit< + EuiComboBoxProps, + 'isDisabled' | 'onChange' | 'options' | 'renderOption' | 'selectedOptions' | 'singleSelection' +> & { + fields: IFieldType[]; + onChange: (fieldName?: string) => void; + value?: string; // index pattern field name + isFieldDisabled?: (field: IFieldType) => boolean; + getFieldDisabledReason?: (field: IFieldType) => string | null; +}; + +export function SingleFieldSelect({ + fields, + getFieldDisabledReason, + isFieldDisabled, + onChange, + value, + ...rest +}: Props) { + function renderOption( + option: EuiComboBoxOptionOption, + searchValue: string, + contentClassName: string + ) { + const content = ( + + + + + + {option.label} + + + ); + + const disabledReason = option.disabled ? getFieldDisabledReason(option.value) : null; + + return disabledReason ? ( + + {content} + + ) : ( + content + ); + } + + const onSelection = (selectedOptions: Array>) => { + onChange(_.get(selectedOptions, '0.value.name')); + }; + + const selectedOptions: Array> = []; + if (value) { + const selectedField = fields.find((field: IFieldType) => { + return field.name === value; + }); + if (selectedField) { + selectedOptions.push({ value: selectedField, label: value }); + } + } + + return ( + + ); +} diff --git a/x-pack/plugins/maps/public/index_pattern_util.js b/x-pack/plugins/maps/public/index_pattern_util.js index 514feeaa220722..6c654ad10581db 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.js +++ b/x-pack/plugins/maps/public/index_pattern_util.js @@ -6,7 +6,7 @@ import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; import { indexPatterns } from '../../../../src/plugins/data/public'; -import { ES_GEO_FIELD_TYPE } from '../common/constants'; +import { ES_GEO_FIELD_TYPE, ES_GEO_FIELD_TYPES } from '../common/constants'; export async function getIndexPatternsFromIds(indexPatternIds = []) { const promises = []; @@ -38,6 +38,12 @@ export function getAggregatableGeoFieldTypes() { return aggregatableFieldTypes; } +export function getGeoFields(fields) { + return fields.filter((field) => { + return !indexPatterns.isNestedField(field) && ES_GEO_FIELD_TYPES.includes(field.type); + }); +} + export function getFieldsWithGeoTileAgg(fields) { return fields.filter(supportsGeoTileAgg); } From 9225872e17ac0d797ba00ba2bfad9897ab5147f2 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 08:56:20 -0600 Subject: [PATCH 2/9] show gold in scaling form --- .../create_source_editor.js | 25 +--- .../__snapshots__/scaling_form.test.tsx.snap | 127 ++++++++++-------- .../es_search_source/create_source_editor.js | 14 +- .../es_search_source/scaling_form.test.tsx | 10 +- .../sources/es_search_source/scaling_form.tsx | 74 +++++----- .../es_search_source/update_source_editor.js | 9 +- ...til.test.js => index_pattern_util.test.ts} | 0 ..._pattern_util.js => index_pattern_util.ts} | 34 ++++- 8 files changed, 172 insertions(+), 121 deletions(-) rename x-pack/plugins/maps/public/{index_pattern_util.test.js => index_pattern_util.test.ts} (100%) rename x-pack/plugins/maps/public/{index_pattern_util.js => index_pattern_util.ts} (61%) diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js index f577b266013802..24edf0251c1535 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/create_source_editor.js @@ -10,11 +10,7 @@ import PropTypes from 'prop-types'; import { ES_GEO_FIELD_TYPES } from '../../../../common/constants'; import { SingleFieldSelect } from '../../../components/single_field_select'; -import { - getIndexPatternService, - getIndexPatternSelectComponent, - getIsGoldPlus, -} from '../../../kibana_services'; +import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; @@ -22,6 +18,7 @@ import { EuiFormRow, EuiSpacer } from '@elastic/eui'; import { getFieldsWithGeoTileAgg, getGeoFields, + getGeoTileAggNotSupportedReason, supportsGeoTileAgg, } from '../../../index_pattern_util'; import { RenderAsSelect } from './render_as_select'; @@ -30,24 +27,6 @@ function doesNotSupportGeoTileAgg(field) { return !supportsGeoTileAgg(field); } -function getGeoTileAggNotSupportedReason(field) { - if (!field.aggregatable) { - return i18n.translate('xpack.maps.geoTileAgg.disabled.docValues', { - defaultMessage: - 'Doc values not enabled for {field.name}. Turn on doc_values in elasticsearch index mapping to enable.', - values: { name: field.name }, - }); - } - - if (!getIsGoldPlus()) { - return i18n.translate('xpack.maps.geoTileAgg.disabled.license', { - defaultMessage: 'Clustering geo_shape fields requires Gold license or higher.', - }); - } - - return null; -} - export class CreateSourceEditor extends Component { static propTypes = { onSourceConfigChange: PropTypes.func.isRequired, diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap index 967225d6f0fdca..2b04da92517561 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should not render clusters option when clustering is not supported 1`] = ` +exports[`should disable clusters option when clustering is not supported 1`] = ` - +
+ + + + + +
- +
+ + + +
- +
+ + + +
{ expect(component).toMatchSnapshot(); }); -test('should not render clusters option when clustering is not supported', async () => { - const component = shallow(); +test('should disable clusters option when clustering is not supported', async () => { + const component = shallow( + + ); expect(component).toMatchSnapshot(); }); diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx index 829c9a1ce439da..d99924db8e3369 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx @@ -12,11 +12,11 @@ import { EuiTitle, EuiSpacer, EuiHorizontalRule, - EuiRadioGroup, + EuiRadio, + EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -// @ts-ignore import { SingleFieldSelect } from '../../../components/single_field_select'; import { getIndexPatternService } from '../../../kibana_services'; // @ts-ignore @@ -38,6 +38,7 @@ interface Props { onChange: (args: OnSourceChangeArgs) => void; scalingType: SCALING_TYPES; supportsClustering: boolean; + clusteringDisabledReason?: string | null; termFields: IFieldType[]; topHitsSplitField?: string; topHitsSize: number; @@ -149,32 +150,30 @@ export class ScalingForm extends Component { ); } - render() { - const scalingOptions = [ - { - id: SCALING_TYPES.LIMIT, - label: i18n.translate('xpack.maps.source.esSearch.limitScalingLabel', { - defaultMessage: 'Limit results to {maxResultWindow}.', - values: { maxResultWindow: this.state.maxResultWindow }, - }), - }, - { - id: SCALING_TYPES.TOP_HITS, - label: i18n.translate('xpack.maps.source.esSearch.useTopHitsLabel', { - defaultMessage: 'Show top hits per entity.', - }), - }, - ]; - if (this.props.supportsClustering) { - scalingOptions.push({ - id: SCALING_TYPES.CLUSTERS, - label: i18n.translate('xpack.maps.source.esSearch.clusterScalingLabel', { + _renderClusteringRadio() { + const clusteringRadio = ( + this._onScalingTypeChange(SCALING_TYPES.CLUSTERS)} + disabled={!this.props.supportsClustering} + /> + ); + return this.props.clusteringDisabledReason ? ( + + {clusteringRadio} + + ) : ( + clusteringRadio + ); + } + + render() { let filterByBoundsSwitch; if (this.props.scalingType !== SCALING_TYPES.CLUSTERS) { filterByBoundsSwitch = ( @@ -212,11 +211,26 @@ export class ScalingForm extends Component { - +
+ this._onScalingTypeChange(SCALING_TYPES.LIMIT)} + /> + this._onScalingTypeChange(SCALING_TYPES.TOP_HITS)} + /> + {this._renderClusteringRadio()} +
{filterByBoundsSwitch} diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js b/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js index 95e48c9629f57b..0701dbbaecdd56 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/update_source_editor.js @@ -12,7 +12,12 @@ import { TooltipSelector } from '../../../components/tooltip_selector'; import { getIndexPatternService } from '../../../kibana_services'; import { i18n } from '@kbn/i18n'; -import { getTermsFields, getSourceFields, supportsGeoTileAgg } from '../../../index_pattern_util'; +import { + getGeoTileAggNotSupportedReason, + getTermsFields, + getSourceFields, + supportsGeoTileAgg, +} from '../../../index_pattern_util'; import { SORT_ORDER } from '../../../../common/constants'; import { ESDocField } from '../../fields/es_doc_field'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -91,6 +96,7 @@ export class UpdateSourceEditor extends Component { this.setState({ supportsClustering: supportsGeoTileAgg(geoField), + clusteringDisabledReason: getGeoTileAggNotSupportedReason(geoField), sourceFields: sourceFields, termFields: getTermsFields(indexPattern.fields), //todo change term fields to use fields sortFields: indexPattern.fields.filter( @@ -201,6 +207,7 @@ export class UpdateSourceEditor extends Component { onChange={this.props.onChange} scalingType={this.props.scalingType} supportsClustering={this.state.supportsClustering} + clusteringDisabledReason={this.state.clusteringDisabledReason} termFields={this.state.termFields} topHitsSplitField={this.props.topHitsSplitField} topHitsSize={this.props.topHitsSize} diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.js b/x-pack/plugins/maps/public/index_pattern_util.test.ts similarity index 100% rename from x-pack/plugins/maps/public/index_pattern_util.test.js rename to x-pack/plugins/maps/public/index_pattern_util.test.ts diff --git a/x-pack/plugins/maps/public/index_pattern_util.js b/x-pack/plugins/maps/public/index_pattern_util.ts similarity index 61% rename from x-pack/plugins/maps/public/index_pattern_util.js rename to x-pack/plugins/maps/public/index_pattern_util.ts index 6c654ad10581db..78318415a1031f 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.js +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -4,11 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IFieldType, IndexPattern } from 'src/plugins/data/public'; +import { i18n } from '@kbn/i18n'; import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; import { indexPatterns } from '../../../../src/plugins/data/public'; import { ES_GEO_FIELD_TYPE, ES_GEO_FIELD_TYPES } from '../common/constants'; -export async function getIndexPatternsFromIds(indexPatternIds = []) { +export function getGeoTileAggNotSupportedReason(field: IFieldType): string | null { + if (!field.aggregatable) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.docValues', { + defaultMessage: 'Clustering requires doc_values.', + }); + } + + if (field.type === ES_GEO_FIELD_TYPE.GEO_SHAPE && !getIsGoldPlus()) { + return i18n.translate('xpack.maps.geoTileAgg.disabled.license', { + defaultMessage: 'Geo_shape clustering requires a Gold license.', + }); + } + + return null; +} + +export async function getIndexPatternsFromIds( + indexPatternIds: string[] = [] +): Promise { const promises = []; indexPatternIds.forEach((id) => { const indexPatternPromise = getIndexPatternService().get(id); @@ -20,7 +40,7 @@ export async function getIndexPatternsFromIds(indexPatternIds = []) { return await Promise.all(promises); } -export function getTermsFields(fields) { +export function getTermsFields(fields: IFieldType[]): IFieldType[] { return fields.filter((field) => { return ( field.aggregatable && @@ -30,7 +50,7 @@ export function getTermsFields(fields) { }); } -export function getAggregatableGeoFieldTypes() { +export function getAggregatableGeoFieldTypes(): string[] { const aggregatableFieldTypes = [ES_GEO_FIELD_TYPE.GEO_POINT]; if (getIsGoldPlus()) { aggregatableFieldTypes.push(ES_GEO_FIELD_TYPE.GEO_SHAPE); @@ -38,17 +58,17 @@ export function getAggregatableGeoFieldTypes() { return aggregatableFieldTypes; } -export function getGeoFields(fields) { +export function getGeoFields(fields: IFieldType[]): IFieldType[] { return fields.filter((field) => { return !indexPatterns.isNestedField(field) && ES_GEO_FIELD_TYPES.includes(field.type); }); } -export function getFieldsWithGeoTileAgg(fields) { +export function getFieldsWithGeoTileAgg(fields: IFieldType[]): IFieldType[] { return fields.filter(supportsGeoTileAgg); } -export function supportsGeoTileAgg(field) { +export function supportsGeoTileAgg(field: IFieldType): boolean { return ( field && field.aggregatable && @@ -58,7 +78,7 @@ export function supportsGeoTileAgg(field) { } // Returns filtered fields list containing only fields that exist in _source. -export function getSourceFields(fields) { +export function getSourceFields(fields: IFieldType[]): IFieldType[] { return fields.filter((field) => { // Multi fields are not stored in _source and only exist in index. const isMultiField = field.subType && field.subType.multi; From 9aef58e638930442b66f757581e335d10e4698e3 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 09:47:24 -0600 Subject: [PATCH 3/9] tslint --- x-pack/plugins/maps/common/constants.ts | 4 +++- .../classes/sources/es_search_source/scaling_form.tsx | 2 +- .../maps/public/components/single_field_select.tsx | 8 ++++---- x-pack/plugins/maps/public/index_pattern_util.test.ts | 5 +++++ x-pack/plugins/maps/public/index_pattern_util.ts | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 0961b5ed5f8ebc..211957d5d288e0 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -99,7 +99,9 @@ export enum ES_GEO_FIELD_TYPE { GEO_SHAPE = 'geo_shape', } -export const ES_GEO_FIELD_TYPES = [ES_GEO_FIELD_TYPE.GEO_POINT, ES_GEO_FIELD_TYPE.GEO_SHAPE]; +// Using strings instead of ES_GEO_FIELD_TYPE enum to avoid typeing errors where IFieldType.type is compared to value +// TODO replace ES_GEO_FIELD_TYPE enum with strings from core kibana. +export const ES_GEO_FIELD_TYPES = ['geo_point', 'geo_shape']; export enum ES_SPATIAL_RELATIONS { INTERSECTS = 'INTERSECTS', diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx index d99924db8e3369..a998fe3569835c 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/scaling_form.tsx @@ -89,7 +89,7 @@ export class ScalingForm extends Component { this.props.onChange({ propName: 'filterByMapBounds', value: event.target.checked }); }; - _onTopHitsSplitFieldChange = (topHitsSplitField: string) => { + _onTopHitsSplitFieldChange = (topHitsSplitField?: string) => { this.props.onChange({ propName: 'topHitsSplitField', value: topHitsSplitField }); }; diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx index d0a6f45da5e1ea..0b8ecec5aa8b45 100644 --- a/x-pack/plugins/maps/public/components/single_field_select.tsx +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -21,7 +21,7 @@ import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; function fieldsToOptions( fields: IFieldType[], - isFieldDisabled?: (field: IField) => boolean + isFieldDisabled?: (field: IFieldType) => boolean ): Array> { if (!fields) { return []; @@ -31,7 +31,7 @@ function fieldsToOptions( .map((field) => { const option: EuiComboBoxOptionOption = { value: field, - label: 'label' in field ? field.label : field.name, + label: field.name, }; if (isFieldDisabled && isFieldDisabled(field)) { option.disabled = true; @@ -70,7 +70,7 @@ export function SingleFieldSelect({ const content = ( - + {option.label} @@ -78,7 +78,7 @@ export function SingleFieldSelect({ ); - const disabledReason = option.disabled ? getFieldDisabledReason(option.value) : null; + const disabledReason = option.disabled ? getFieldDisabledReason(option.value!) : null; return disabledReason ? ( diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.ts b/x-pack/plugins/maps/public/index_pattern_util.test.ts index 4fa9eb3cadb494..cd5447faa426fd 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.test.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.test.ts @@ -18,6 +18,7 @@ describe('getSourceFields', () => { const fields = [ { name: 'agent', + type: 'string', }, { name: 'agent.keyword', @@ -26,6 +27,7 @@ describe('getSourceFields', () => { parent: 'agent', }, }, + type: 'string', }, ]; const sourceFields = getSourceFields(fields); @@ -37,6 +39,7 @@ describe('Gold+ licensing', () => { const testStubs = [ { field: { + name: 'location', type: 'geo_point', aggregatable: true, }, @@ -45,6 +48,7 @@ describe('Gold+ licensing', () => { }, { field: { + name: 'location', type: 'geo_shape', aggregatable: false, }, @@ -53,6 +57,7 @@ describe('Gold+ licensing', () => { }, { field: { + name: 'location', type: 'geo_shape', aggregatable: true, }, diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts index 78318415a1031f..bc19ee08101089 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -29,7 +29,7 @@ export function getGeoTileAggNotSupportedReason(field: IFieldType): string | nul export async function getIndexPatternsFromIds( indexPatternIds: string[] = [] ): Promise { - const promises = []; + const promises: Promise = []; indexPatternIds.forEach((id) => { const indexPatternPromise = getIndexPatternService().get(id); if (indexPatternPromise) { From 79f66b19add8b26998137a1b7c8449375b57b7dd Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 10:54:53 -0600 Subject: [PATCH 4/9] more tslint changes --- .../maps/public/components/single_field_select.tsx | 3 ++- .../public/embeddable/map_embeddable_factory.ts | 6 +++--- x-pack/plugins/maps/public/index_pattern_util.ts | 13 +++++-------- x-pack/plugins/maps/public/kibana_services.d.ts | 6 +++--- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx index 0b8ecec5aa8b45..bc209afbc58e97 100644 --- a/x-pack/plugins/maps/public/components/single_field_select.tsx +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -78,7 +78,8 @@ export function SingleFieldSelect({ ); - const disabledReason = option.disabled ? getFieldDisabledReason(option.value!) : null; + const disabledReason = + option.disabled && getFieldDisabledReason ? getFieldDisabledReason(option.value!) : null; return disabledReason ? ( diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts index c6103bcc73fc53..c5629908193b00 100644 --- a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -21,6 +21,8 @@ import { MapEmbeddableConfig, MapEmbeddableInput } from './types'; import { MapEmbeddableOutput } from './map_embeddable'; import { RenderToolTipContent } from '../classes/tooltips/tooltip_property'; import { EventHandlers } from '../reducers/non_serializable_instances'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IndexPatternsService } from '../../../../../src/plugins/data/public/index_patterns/index_patterns/index_patterns'; let whenModulesLoadedPromise: Promise; @@ -33,9 +35,7 @@ let MapEmbeddable: new ( eventHandlers?: EventHandlers ) => Embeddable; -let getIndexPatternService: () => { - get: (id: string) => IIndexPattern | undefined; -}; +let getIndexPatternService: () => IndexPatternsService; let getHttp: () => any; let getMapsCapabilities: () => any; let createMapStore: () => MapStore; diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts index bc19ee08101089..9b0dfb3c0d9b9a 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -29,12 +29,9 @@ export function getGeoTileAggNotSupportedReason(field: IFieldType): string | nul export async function getIndexPatternsFromIds( indexPatternIds: string[] = [] ): Promise { - const promises: Promise = []; + const promises: Array> = []; indexPatternIds.forEach((id) => { - const indexPatternPromise = getIndexPatternService().get(id); - if (indexPatternPromise) { - promises.push(indexPatternPromise); - } + promises.push(getIndexPatternService().get(id)); }); return await Promise.all(promises); @@ -68,10 +65,10 @@ export function getFieldsWithGeoTileAgg(fields: IFieldType[]): IFieldType[] { return fields.filter(supportsGeoTileAgg); } -export function supportsGeoTileAgg(field: IFieldType): boolean { +export function supportsGeoTileAgg(field?: IFieldType): boolean { return ( - field && - field.aggregatable && + !!field && + !!field.aggregatable && !indexPatterns.isNestedField(field) && getAggregatableGeoFieldTypes().includes(field.type) ); diff --git a/x-pack/plugins/maps/public/kibana_services.d.ts b/x-pack/plugins/maps/public/kibana_services.d.ts index d45d7286ebf684..a4631cc7c536c2 100644 --- a/x-pack/plugins/maps/public/kibana_services.d.ts +++ b/x-pack/plugins/maps/public/kibana_services.d.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { IIndexPattern, DataPublicPluginStart } from 'src/plugins/data/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IndexPatternsService } from '../../../../src/plugins/data/public/index_patterns/index_patterns/index_patterns'; import { MapsConfigType } from '../config'; import { MapsLegacyConfigType } from '../../../../src/plugins/maps_legacy/public'; @@ -14,9 +16,7 @@ export function getIndexPatternSelectComponent(): any; export function getHttp(): any; export function getTimeFilter(): any; export function getToasts(): any; -export function getIndexPatternService(): { - get: (id: string) => IIndexPattern | undefined; -}; +export function getIndexPatternService(): IndexPatternsService; export function getAutocompleteService(): any; export function getSavedObjectsClient(): any; export function getMapsCapabilities(): any; From 809fdd46682ef88941cc5a41b315e7154640f384 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 14:15:48 -0600 Subject: [PATCH 5/9] fix jest test --- x-pack/plugins/maps/public/index_pattern_util.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.ts b/x-pack/plugins/maps/public/index_pattern_util.test.ts index cd5447faa426fd..27b0a4aac9bf74 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.test.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.test.ts @@ -31,7 +31,7 @@ describe('getSourceFields', () => { }, ]; const sourceFields = getSourceFields(fields); - expect(sourceFields).toEqual([{ name: 'agent' }]); + expect(sourceFields).toEqual([{ name: 'agent', type: 'string' }]); }); }); From 8529cb9d314abab6c40a5f8dc86289e0fc824b91 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 16:22:34 -0600 Subject: [PATCH 6/9] fix functional test by handling fields prop being undefined --- .../plugins/maps/public/components/single_field_select.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx index bc209afbc58e97..9d200fc4e44f90 100644 --- a/x-pack/plugins/maps/public/components/single_field_select.tsx +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -21,7 +21,7 @@ import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; function fieldsToOptions( fields: IFieldType[], - isFieldDisabled?: (field: IFieldType) => boolean + isFieldDisabled?: (field?: IFieldType) => boolean ): Array> { if (!fields) { return []; @@ -47,7 +47,7 @@ type Props = Omit< EuiComboBoxProps, 'isDisabled' | 'onChange' | 'options' | 'renderOption' | 'selectedOptions' | 'singleSelection' > & { - fields: IFieldType[]; + fields?: IFieldType[]; onChange: (fieldName?: string) => void; value?: string; // index pattern field name isFieldDisabled?: (field: IFieldType) => boolean; @@ -95,7 +95,7 @@ export function SingleFieldSelect({ }; const selectedOptions: Array> = []; - if (value) { + if (value && fields) { const selectedField = fields.find((field: IFieldType) => { return field.name === value; }); From f125deacaad3b69aed6263d50233c2ed01fe5fbc Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 9 Jun 2020 16:26:41 -0600 Subject: [PATCH 7/9] tslint fixes - that thing is slow to run --- x-pack/plugins/maps/public/components/single_field_select.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/maps/public/components/single_field_select.tsx b/x-pack/plugins/maps/public/components/single_field_select.tsx index 9d200fc4e44f90..eb3a28be0efc01 100644 --- a/x-pack/plugins/maps/public/components/single_field_select.tsx +++ b/x-pack/plugins/maps/public/components/single_field_select.tsx @@ -20,8 +20,8 @@ import { IFieldType } from 'src/plugins/data/public'; import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; function fieldsToOptions( - fields: IFieldType[], - isFieldDisabled?: (field?: IFieldType) => boolean + fields?: IFieldType[], + isFieldDisabled?: (field: IFieldType) => boolean ): Array> { if (!fields) { return []; From f8ab7055ad5239ebad45f0d7459a47f98b363daf Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 11 Jun 2020 15:25:14 -0600 Subject: [PATCH 8/9] review feedback --- x-pack/plugins/maps/common/constants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 211957d5d288e0..ad99780a7d32fb 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -100,7 +100,6 @@ export enum ES_GEO_FIELD_TYPE { } // Using strings instead of ES_GEO_FIELD_TYPE enum to avoid typeing errors where IFieldType.type is compared to value -// TODO replace ES_GEO_FIELD_TYPE enum with strings from core kibana. export const ES_GEO_FIELD_TYPES = ['geo_point', 'geo_shape']; export enum ES_SPATIAL_RELATIONS { From 9a2d21717a51bebd9f46bad67796c6435a8c910e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 11 Jun 2020 16:50:20 -0600 Subject: [PATCH 9/9] update doc_values missing copy --- x-pack/plugins/maps/public/index_pattern_util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts index 9b0dfb3c0d9b9a..e65e37ef19809c 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -13,7 +13,8 @@ import { ES_GEO_FIELD_TYPE, ES_GEO_FIELD_TYPES } from '../common/constants'; export function getGeoTileAggNotSupportedReason(field: IFieldType): string | null { if (!field.aggregatable) { return i18n.translate('xpack.maps.geoTileAgg.disabled.docValues', { - defaultMessage: 'Clustering requires doc_values.', + defaultMessage: + 'Clustering requires aggregations. Enable aggregations by setting doc_values to true.', }); }