diff --git a/src/legacy/core_plugins/metrics/public/components/aggs/agg.js b/src/legacy/core_plugins/metrics/public/components/aggs/agg.js index 734ec440433e..aa0cbc535ed1 100644 --- a/src/legacy/core_plugins/metrics/public/components/aggs/agg.js +++ b/src/legacy/core_plugins/metrics/public/components/aggs/agg.js @@ -22,17 +22,26 @@ import React from 'react'; import aggToComponent from '../lib/agg_to_component'; import { sortable } from 'react-anything-sortable'; import { UnsupportedAgg } from './unsupported_agg'; +import { TemporaryUnsupportedAgg } from './temporary_unsupported_agg'; + +import { isMetricEnabled } from '../../lib/check_ui_restrictions'; function Agg(props) { - const { model } = props; + const { model, uiRestrictions } = props; + let Component = aggToComponent[model.type]; + if (!Component) { Component = UnsupportedAgg; + } else if (!isMetricEnabled(model.type, uiRestrictions)) { + Component = TemporaryUnsupportedAgg; } + const style = { cursor: 'default', ...props.style, }; + return (
); @@ -70,6 +80,7 @@ Agg.propTypes = { series: PropTypes.object, siblings: PropTypes.array, sortData: PropTypes.string, + uiRestrictions: PropTypes.object, }; export default sortable(Agg); diff --git a/src/legacy/core_plugins/metrics/public/components/aggs/agg_select.js b/src/legacy/core_plugins/metrics/public/components/aggs/agg_select.js index 5def89a0e0a6..66e61e8f7f99 100644 --- a/src/legacy/core_plugins/metrics/public/components/aggs/agg_select.js +++ b/src/legacy/core_plugins/metrics/public/components/aggs/agg_select.js @@ -22,6 +22,7 @@ import React from 'react'; import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { injectI18n } from '@kbn/i18n/react'; +import { isMetricEnabled } from '../../lib/check_ui_restrictions'; const metricAggs = [ { @@ -169,41 +170,45 @@ function filterByPanelType(panelType) { } function AggSelectUi(props) { - const { siblings, panelType, value, onChange, intl, ...rest } = props; - const selectedOption = allAggOptions.find(option => { - return value === option.value; + const { siblings, panelType, value, onChange, intl, uiRestrictions, ...rest } = props; + + const selectedOptions = allAggOptions.filter(option => { + return value === option.value && isMetricEnabled(option.value, uiRestrictions); }); - const selectedOptions = selectedOption ? [selectedOption] : []; let enablePipelines = siblings.some( s => !!metricAggs.find(m => m.value === s.type) ); + if (siblings.length <= 1) enablePipelines = false; let options; if (panelType === 'metrics') { options = metricAggs; } else { + const disableSiblingAggs = agg => ({ ...agg, disabled: !enablePipelines || !isMetricEnabled(agg.value, uiRestrictions) }); + options = [ { label: intl.formatMessage({ id: 'tsvb.aggSelect.aggGroups.metricAggLabel', defaultMessage: 'Metric Aggregations' }), - options: metricAggs, + options: metricAggs + .map(agg => ({ ...agg, disabled: !isMetricEnabled(agg.value, uiRestrictions) })), }, { label: intl.formatMessage({ id: 'tsvb.aggSelect.aggGroups.parentPipelineAggLabel', defaultMessage: 'Parent Pipeline Aggregations' }), options: pipelineAggs .filter(filterByPanelType(panelType)) - .map(agg => ({ ...agg, disabled: !enablePipelines })), + .map(disableSiblingAggs), }, { label: intl.formatMessage({ id: 'tsvb.aggSelect.aggGroups.siblingPipelineAggLabel', defaultMessage: 'Sibling Pipeline Aggregations' }), - options: siblingAggs.map(agg => ({ ...agg, disabled: !enablePipelines })), + options: siblingAggs.map(disableSiblingAggs), }, { label: intl.formatMessage({ id: 'tsvb.aggSelect.aggGroups.specialAggLabel', defaultMessage: 'Special Aggregations' }), - options: specialAggs.map(agg => ({ ...agg, disabled: !enablePipelines })), + options: specialAggs.map(disableSiblingAggs), }, ]; } @@ -233,6 +238,7 @@ AggSelectUi.propTypes = { panelType: PropTypes.string, siblings: PropTypes.array, value: PropTypes.string, + uiRestrictions: PropTypes.object, }; const AggSelect = injectI18n(AggSelectUi); diff --git a/src/legacy/core_plugins/metrics/public/components/aggs/field_select.js b/src/legacy/core_plugins/metrics/public/components/aggs/field_select.js index cd9b9842bc96..f0690ec68223 100644 --- a/src/legacy/core_plugins/metrics/public/components/aggs/field_select.js +++ b/src/legacy/core_plugins/metrics/public/components/aggs/field_select.js @@ -24,26 +24,39 @@ import { } from '@elastic/eui'; import generateByTypeFilter from '../lib/generate_by_type_filter'; import { injectI18n } from '@kbn/i18n/react'; +import { isFieldEnabled } from '../../lib/check_ui_restrictions'; + +function FieldSelectUi({ + type, + fields, + indexPattern, + value, + onChange, + disabled, + restrict, + intl, + uiRestrictions, + ...rest +}) { -function FieldSelectUi(props) { - const { type, fields, indexPattern, value, onChange, disabled, restrict, intl, ...rest } = props; if (type === 'count') { return null; } - const options = (fields[indexPattern] || []) - .filter(generateByTypeFilter(restrict)) - .map(field => { - return { label: field.name, value: field.name }; - }); - const selectedOption = options.find(option => { - return value === option.value; - }); + const typeFilter = generateByTypeFilter(restrict); + const options = (fields[indexPattern] || []) + .filter(field => typeFilter(field) && isFieldEnabled(field.name, type, uiRestrictions)) + .map(field => ({ label: field.name, value: field.name })); + + const selectedOption = options.find(option => value === option.value); const selectedOptions = selectedOption ? [selectedOption] : []; return ( @@ -85,6 +85,7 @@ function StandardAgg(props) { indexPattern={indexPattern} value={model.field} onChange={handleSelectChange('field')} + uiRestrictions={uiRestrictions} fullWidth /> @@ -95,7 +96,6 @@ function StandardAgg(props) { ); - } StandardAgg.propTypes = { @@ -108,6 +108,7 @@ StandardAgg.propTypes = { panel: PropTypes.object, series: PropTypes.object, siblings: PropTypes.array, + uiRestrictions: PropTypes.object, }; export default StandardAgg; diff --git a/src/legacy/core_plugins/metrics/public/components/aggs/std_sibling.js b/src/legacy/core_plugins/metrics/public/components/aggs/std_sibling.js index 26fd7d8e4a1f..39ec2d9a1840 100644 --- a/src/legacy/core_plugins/metrics/public/components/aggs/std_sibling.js +++ b/src/legacy/core_plugins/metrics/public/components/aggs/std_sibling.js @@ -128,6 +128,7 @@ const StandardSiblingAggUi = props => { panelType={props.panel.type} siblings={props.siblings} value={model.type} + uiRestrictions={props.uiRestrictions} onChange={handleSelectChange('type')} /> @@ -165,6 +166,7 @@ StandardSiblingAggUi.propTypes = { panel: PropTypes.object, series: PropTypes.object, siblings: PropTypes.array, + uiRestrictions: PropTypes.object, }; export const StandardSiblingAgg = injectI18n(StandardSiblingAggUi); diff --git a/src/legacy/core_plugins/metrics/public/components/aggs/temporary_unsupported_agg.js b/src/legacy/core_plugins/metrics/public/components/aggs/temporary_unsupported_agg.js new file mode 100644 index 000000000000..eedc10bdbb03 --- /dev/null +++ b/src/legacy/core_plugins/metrics/public/components/aggs/temporary_unsupported_agg.js @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import AggRow from './agg_row'; +import React from 'react'; +import { EuiCode, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export function TemporaryUnsupportedAgg(props) { + return ( + + + + {props.model.type}) }} + /> + + + + ); +} diff --git a/src/legacy/core_plugins/metrics/public/components/lib/create_agg_row_render.js b/src/legacy/core_plugins/metrics/public/components/lib/create_agg_row_render.js index 6fa3cea6ad3a..91ee48576859 100644 --- a/src/legacy/core_plugins/metrics/public/components/lib/create_agg_row_render.js +++ b/src/legacy/core_plugins/metrics/public/components/lib/create_agg_row_render.js @@ -25,8 +25,9 @@ import Agg from '../aggs/agg'; export default function createAggRowRender(props) { return (row, index, items) => { - const { panel, model, fields } = props; + const { panel, model, fields, uiRestrictions } = props; const changeHandler = seriesChangeHandler(props, items); + return ( ); diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/gauge.js b/src/legacy/core_plugins/metrics/public/components/panel_config/gauge.js index e1fe00d12345..ebc904e1894d 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/gauge.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/gauge.js @@ -107,6 +107,7 @@ class GaugePanelConfigUi extends Component { limit={1} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> ); @@ -340,6 +341,7 @@ GaugePanelConfigUi.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, + visData$: PropTypes.object, }; const GaugePanelConfig = injectI18n(GaugePanelConfigUi); diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/markdown.js b/src/legacy/core_plugins/metrics/public/components/panel_config/markdown.js index f0ff628770b4..bcad32017c6b 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/markdown.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/markdown.js @@ -110,6 +110,7 @@ class MarkdownPanelConfigUi extends Component { fields={this.props.fields} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> ); @@ -315,7 +316,8 @@ MarkdownPanelConfigUi.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, - dateFormat: PropTypes.string + dateFormat: PropTypes.string, + visData$: PropTypes.object, }; const MarkdownPanelConfig = injectI18n(MarkdownPanelConfigUi); diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/metric.js b/src/legacy/core_plugins/metrics/public/components/panel_config/metric.js index 06cf98a0ad19..edc7e8cd84f1 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/metric.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/metric.js @@ -76,6 +76,7 @@ class MetricPanelConfig extends Component { limit={2} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> ); @@ -191,6 +192,7 @@ MetricPanelConfig.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, + visData$: PropTypes.object, }; export default MetricPanelConfig; diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/table.js b/src/legacy/core_plugins/metrics/public/components/panel_config/table.js index d2663beb23db..cc993d520ced 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/table.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/table.js @@ -161,6 +161,7 @@ class TablePanelConfig extends Component { fields={this.props.fields} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> @@ -280,6 +281,7 @@ TablePanelConfig.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, + visData$: PropTypes.object, }; export default TablePanelConfig; diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/timeseries.js b/src/legacy/core_plugins/metrics/public/components/panel_config/timeseries.js index 6cc0db1fe761..2f1d23c5211e 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/timeseries.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/timeseries.js @@ -119,6 +119,7 @@ class TimeseriesPanelConfigUi extends Component { fields={this.props.fields} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> ); @@ -379,6 +380,7 @@ TimeseriesPanelConfigUi.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, + visData$: PropTypes.object, }; const TimeseriesPanelConfig = injectI18n(TimeseriesPanelConfigUi); diff --git a/src/legacy/core_plugins/metrics/public/components/panel_config/top_n.js b/src/legacy/core_plugins/metrics/public/components/panel_config/top_n.js index 2f7bbb007053..de9bc7284c47 100644 --- a/src/legacy/core_plugins/metrics/public/components/panel_config/top_n.js +++ b/src/legacy/core_plugins/metrics/public/components/panel_config/top_n.js @@ -77,6 +77,7 @@ class TopNPanelConfig extends Component { fields={this.props.fields} model={this.props.model} name={this.props.name} + visData$={this.props.visData$} onChange={this.props.onChange} /> ); @@ -246,6 +247,7 @@ TopNPanelConfig.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, + visData$: PropTypes.object, }; export default TopNPanelConfig; diff --git a/src/legacy/core_plugins/metrics/public/components/series.js b/src/legacy/core_plugins/metrics/public/components/series.js index ada6f9b3b743..eeb1bbee308f 100644 --- a/src/legacy/core_plugins/metrics/public/components/series.js +++ b/src/legacy/core_plugins/metrics/public/components/series.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import _ from 'lodash'; +import { assign, get } from 'lodash'; import timeseries from './vis_types/timeseries/series'; import metric from './vis_types/metric/series'; @@ -46,7 +46,10 @@ class Series extends Component { this.state = { visible: true, selectedTab: 'metrics', + uiRestrictions: undefined, }; + + this.visDataSubscription = null; } switchTab = (selectedTab) => { @@ -56,7 +59,7 @@ class Series extends Component { handleChange = (part) => { if (this.props.onChange) { const { model } = this.props; - const doc = _.assign({}, model, part); + const doc = assign({}, model, part); this.props.onChange(doc); } }; @@ -71,12 +74,25 @@ class Series extends Component { toggleVisible = (e) => { e.preventDefault(); - this.setState({ visible: !this.state.visible }); + + this.setState({ + visible: !this.state.visible + }); }; + componentDidMount() { + if (this.props.visData$) { + this.visDataSubscription = this.props.visData$ + .subscribe(visData => this.setState({ + uiRestrictions: get(visData, 'uiRestrictions') + })); + } + } + render() { const { panel } = this.props; const Component = lookup[panel.type]; + if (Component) { const params = { className: this.props.className, @@ -98,6 +114,7 @@ class Series extends Component { selectedTab: this.state.selectedTab, sortData: this.props.sortData, style: this.props.style, + uiRestrictions: this.state.uiRestrictions, switchTab: this.switchTab, toggleVisible: this.toggleVisible, togglePanelActivation: this.togglePanelActivation, @@ -116,6 +133,11 @@ class Series extends Component { ); } + componentWillUnmount() { + if (this.visDataSubscription) { + this.visDataSubscription.unsubscribe(); + } + } } Series.defaultProps = { @@ -139,6 +161,7 @@ Series.propTypes = { onTouchStart: PropTypes.func, model: PropTypes.object, panel: PropTypes.object, + visData$: PropTypes.object, sortData: PropTypes.string, }; diff --git a/src/legacy/core_plugins/metrics/public/components/series_editor.js b/src/legacy/core_plugins/metrics/public/components/series_editor.js index c48efffb4f7e..a339108a058b 100644 --- a/src/legacy/core_plugins/metrics/public/components/series_editor.js +++ b/src/legacy/core_plugins/metrics/public/components/series_editor.js @@ -72,6 +72,7 @@ class SeriesEditor extends Component { onClone={() => this.handleClone(row)} onDelete={handleDelete.bind(null, props, row)} onShouldSortItem={(direction) => this.sortSeries(index, direction, allSeries)} + visData$={this.props.visData$} model={row} panel={model} sortData={row.id} @@ -115,7 +116,8 @@ SeriesEditor.propTypes = { limit: PropTypes.number, model: PropTypes.object, name: PropTypes.string, - onChange: PropTypes.func + onChange: PropTypes.func, + visData$: PropTypes.object, }; export default SeriesEditor; diff --git a/src/legacy/core_plugins/metrics/public/components/split.js b/src/legacy/core_plugins/metrics/public/components/split.js index 5b5fc1f28d3e..c6e4de601510 100644 --- a/src/legacy/core_plugins/metrics/public/components/split.js +++ b/src/legacy/core_plugins/metrics/public/components/split.js @@ -20,71 +20,77 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import uuid from 'uuid'; +import { get } from 'lodash'; import { SplitByTerms } from './splits/terms'; import { SplitByFilter } from './splits/filter'; -import SplitByFilters from './splits/filters'; -import SplitByEverything from './splits/everything'; +import { SplitByFilters } from './splits/filters'; +import { SplitByEverything } from './splits/everything'; +import { SplitUnsupported } from './splits/unsupported_split'; +import { isGroupByFieldsEnabled } from '../lib/check_ui_restrictions'; + +const SPLIT_MODES = { + FILTERS: 'filters', + FILTER: 'filter', + TERMS: 'terms', + EVERYTHING: 'everything', +}; class Split extends Component { - componentWillReceiveProps(nextProps) { const { model } = nextProps; if (model.split_mode === 'filters' && !model.split_filters) { this.props.onChange({ split_filters: [ - { color: model.color, id: uuid.v1() } - ] + { color: model.color, id: uuid.v1() }, + ], }); } } + getComponent(splitMode, uiRestrictions) { + if (!isGroupByFieldsEnabled(splitMode, uiRestrictions)) { + return SplitUnsupported; + } + + switch (splitMode) { + case SPLIT_MODES.TERMS: + return SplitByTerms; + case SPLIT_MODES.FILTER: + return SplitByFilter; + case SPLIT_MODES.FILTERS: + return SplitByFilters; + default: + return SplitByEverything; + } + } + render() { - const { model, panel } = this.props; + const { model, panel, uiRestrictions } = this.props; const indexPattern = model.override_index_pattern && model.series_index_pattern || panel.index_pattern; - if (model.split_mode === 'filter') { - return ( - - ); - } - if (model.split_mode === 'filters') { - return ( - - ); - } - if (model.split_mode === 'terms') { - return ( - - ); - } - return ( - - ); - } + const splitMode = get(this.props, 'model.split_mode', SPLIT_MODES.EVERYTHING); + + const Component = this.getComponent(splitMode, uiRestrictions); + + return ( + ); + } } Split.propTypes = { fields: PropTypes.object, model: PropTypes.object, onChange: PropTypes.func, - panel: PropTypes.object + panel: PropTypes.object, }; export default Split; diff --git a/src/legacy/core_plugins/metrics/public/components/splits/__snapshots__/terms.test.js.snap b/src/legacy/core_plugins/metrics/public/components/splits/__snapshots__/terms.test.js.snap index 611dbbdbe008..2fd00f2d0f28 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/__snapshots__/terms.test.js.snap +++ b/src/legacy/core_plugins/metrics/public/components/splits/__snapshots__/terms.test.js.snap @@ -55,6 +55,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js } indexPattern="kibana_sample_data_flights" onChange={[Function]} + type="terms" value="OriginCityName" /> diff --git a/src/legacy/core_plugins/metrics/public/components/splits/everything.js b/src/legacy/core_plugins/metrics/public/components/splits/everything.js index e6aead092faa..e4ab89579e52 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/everything.js +++ b/src/legacy/core_plugins/metrics/public/components/splits/everything.js @@ -18,14 +18,14 @@ */ import createSelectHandler from '../lib/create_select_handler'; -import GroupBySelect from './group_by_select'; +import { GroupBySelect } from './group_by_select'; import PropTypes from 'prop-types'; import React from 'react'; import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -function SplitByEverything(props) { - const { onChange, model } = props; +export const SplitByEverything = (props) => { + const { onChange, model, uiRestrictions } = props; const htmlId = htmlIdGenerator(); const handleSelectChange = createSelectHandler(onChange); return ( @@ -41,18 +41,18 @@ function SplitByEverything(props) { ); - -} +}; SplitByEverything.propTypes = { model: PropTypes.object, - onChange: PropTypes.func + onChange: PropTypes.func, + uiRestrictions: PropTypes.object, }; -export default SplitByEverything; diff --git a/src/legacy/core_plugins/metrics/public/components/splits/filter.js b/src/legacy/core_plugins/metrics/public/components/splits/filter.js index d7c6cfb60e1a..0a73c6e9c397 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/filter.js +++ b/src/legacy/core_plugins/metrics/public/components/splits/filter.js @@ -19,14 +19,14 @@ import createTextHandler from '../lib/create_text_handler'; import createSelectHandler from '../lib/create_select_handler'; -import GroupBySelect from './group_by_select'; +import { GroupBySelect } from './group_by_select'; import PropTypes from 'prop-types'; import React from 'react'; import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; export const SplitByFilter = props => { - const { onChange } = props; + const { onChange, uiRestrictions } = props; const defaults = { filter: '' }; const model = { ...defaults, ...props.model }; const htmlId = htmlIdGenerator(); @@ -45,6 +45,7 @@ export const SplitByFilter = props => { @@ -68,5 +69,6 @@ export const SplitByFilter = props => { SplitByFilter.propTypes = { model: PropTypes.object, - onChange: PropTypes.func + onChange: PropTypes.func, + uiRestrictions: PropTypes.object, }; diff --git a/src/legacy/core_plugins/metrics/public/components/splits/filters.js b/src/legacy/core_plugins/metrics/public/components/splits/filters.js index bc7c412690d3..16532a41d49b 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/filters.js +++ b/src/legacy/core_plugins/metrics/public/components/splits/filters.js @@ -18,18 +18,18 @@ */ import createSelectHandler from '../lib/create_select_handler'; -import GroupBySelect from './group_by_select'; +import { GroupBySelect } from './group_by_select'; import FilterItems from './filter_items'; import PropTypes from 'prop-types'; import React from 'react'; import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -function SplitByFilters(props) { - const { onChange, model } = props; +export const SplitByFilters = (props) => { + const { onChange, model, uiRestrictions } = props; const htmlId = htmlIdGenerator(); const handleSelectChange = createSelectHandler(onChange); - return( + return (
@@ -43,6 +43,7 @@ function SplitByFilters(props) { @@ -55,11 +56,10 @@ function SplitByFilters(props) { />
); -} +}; SplitByFilters.propTypes = { model: PropTypes.object, - onChange: PropTypes.func + onChange: PropTypes.func, + uiRestrictions: PropTypes.object, }; - -export default SplitByFilters; diff --git a/src/legacy/core_plugins/metrics/public/components/splits/group_by_select.js b/src/legacy/core_plugins/metrics/public/components/splits/group_by_select.js index 9d11e3fae2ea..512da270d47f 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/group_by_select.js +++ b/src/legacy/core_plugins/metrics/public/components/splits/group_by_select.js @@ -23,31 +23,43 @@ import { EuiComboBox, } from '@elastic/eui'; import { injectI18n } from '@kbn/i18n/react'; +import { isGroupByFieldsEnabled } from '../../lib/check_ui_restrictions'; function GroupBySelectUi(props) { - const { intl } = props; - const modeOptions = [ + const { intl, uiRestrictions } = props; + const modeOptions = ([ { - label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.everythingLabel', defaultMessage: 'Everything' }), - value: 'everything' + label: intl.formatMessage({ + id: 'tsvb.splits.groupBySelect.modeOptions.everythingLabel', + defaultMessage: 'Everything', + }), + value: 'everything', }, { label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.filterLabel', defaultMessage: 'Filter' }), - value: 'filter' + value: 'filter', }, { - label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.filtersLabel', defaultMessage: 'Filters' }), - value: 'filters' + label: intl.formatMessage({ + id: 'tsvb.splits.groupBySelect.modeOptions.filtersLabel', + defaultMessage: 'Filters', + }), + value: 'filters', }, { label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.termsLabel', defaultMessage: 'Terms' }), - value: 'terms' - } - ]; + value: 'terms', + }, + ]).map(field => ({ + ...field, + disabled: !isGroupByFieldsEnabled(field.value, uiRestrictions), + })); + const selectedValue = props.value || 'everything'; const selectedOption = modeOptions.find(option => { return selectedValue === option.value; }); + return ( ); - } GroupBySelectUi.propTypes = { onChange: PropTypes.func, - value: PropTypes.string + value: PropTypes.string, + uiRestrictions: PropTypes.object, }; -const GroupBySelect = injectI18n(GroupBySelectUi); -export default GroupBySelect; +export const GroupBySelect = injectI18n(GroupBySelectUi); diff --git a/src/legacy/core_plugins/metrics/public/components/splits/terms.js b/src/legacy/core_plugins/metrics/public/components/splits/terms.js index 295ff3a8a8a9..bb2c994b0945 100644 --- a/src/legacy/core_plugins/metrics/public/components/splits/terms.js +++ b/src/legacy/core_plugins/metrics/public/components/splits/terms.js @@ -20,18 +20,26 @@ import PropTypes from 'prop-types'; import React from 'react'; import { get, find } from 'lodash'; -import GroupBySelect from './group_by_select'; +import { GroupBySelect } from './group_by_select'; import createTextHandler from '../lib/create_text_handler'; import createSelectHandler from '../lib/create_select_handler'; import FieldSelect from '../aggs/field_select'; import MetricSelect from '../aggs/metric_select'; -import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldNumber, EuiComboBox, EuiFieldText } from '@elastic/eui'; +import { + htmlIdGenerator, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldNumber, + EuiComboBox, + EuiFieldText +} from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; import { FIELD_TYPES } from '../../../common/field_types'; const DEFAULTS = { terms_direction: 'desc', terms_size: 10, terms_order_by: '_count' }; -export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesModel, fields }) => { +export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesModel, fields, uiRestrictions }) => { const htmlId = htmlIdGenerator(); const handleTextChange = createTextHandler(onChange); const handleSelectChange = createSelectHandler(onChange); @@ -76,6 +84,7 @@ export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesMode @@ -93,6 +102,8 @@ export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesMode onChange={handleSelectChange('terms_field')} value={model.terms_field} fields={fields} + uiRestrictions={uiRestrictions} + type={'terms'} /> @@ -186,7 +197,8 @@ SplitByTermsUI.propTypes = { model: PropTypes.object, onChange: PropTypes.func, indexPattern: PropTypes.string, - fields: PropTypes.object + fields: PropTypes.object, + uiRestrictions: PropTypes.object, }; export const SplitByTerms = injectI18n(SplitByTermsUI); diff --git a/src/legacy/core_plugins/metrics/public/components/splits/unsupported_split.js b/src/legacy/core_plugins/metrics/public/components/splits/unsupported_split.js new file mode 100644 index 000000000000..2749441f86c8 --- /dev/null +++ b/src/legacy/core_plugins/metrics/public/components/splits/unsupported_split.js @@ -0,0 +1,70 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import createSelectHandler from '../lib/create_select_handler'; +import { GroupBySelect } from './group_by_select'; +import PropTypes from 'prop-types'; +import React from 'react'; +import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiCode, EuiTitle } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +export const SplitUnsupported = (props) => { + const { onChange, model, uiRestrictions } = props; + const htmlId = htmlIdGenerator(); + const handleSelectChange = createSelectHandler(onChange); + return ( + + + )} + > + + + + + + + {model.split_mode}) }} + /> + + + + + ); +}; + +SplitUnsupported.propTypes = { + model: PropTypes.object, + onChange: PropTypes.func, + uiRestrictions: PropTypes.object, +}; + + diff --git a/src/legacy/core_plugins/metrics/public/components/vis_editor.js b/src/legacy/core_plugins/metrics/public/components/vis_editor.js index 0632ef43a811..e8f4857cac87 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_editor.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_editor.js @@ -43,7 +43,7 @@ class VisEditor extends Component { extractedIndexPatterns: [''], }; this.onBrush = brushHandler(props.vis.API.timeFilter); - this.visDataSubject = new Rx.Subject(); + this.visDataSubject = new Rx.BehaviorSubject(this.props.visData); this.visData$ = this.visDataSubject.asObservable().pipe(share()); } @@ -79,8 +79,8 @@ class VisEditor extends Component { } if (this.props.isEditorMode) { - const { params, fields } = this.props.vis; - const extractedIndexPatterns = extractIndexPatterns(params, fields); + const { params } = this.props.vis; + const extractedIndexPatterns = extractIndexPatterns(params); if (!isEqual(this.state.extractedIndexPatterns, extractedIndexPatterns)) { fetchFields(extractedIndexPatterns) diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/gauge/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/gauge/series.js index 4dfb4292a314..047543082ce6 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/gauge/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/gauge/series.js @@ -41,7 +41,8 @@ function GaugeSeriesUi(props) { disableAdd, selectedTab, visible, - intl + intl, + uiRestrictions } = props; const defaults = { label: '' }; @@ -78,6 +79,7 @@ function GaugeSeriesUi(props) { fields={fields} panel={panel} model={model} + uiRestrictions={uiRestrictions} /> @@ -227,6 +229,7 @@ GaugeSeriesUi.propTypes = { switchTab: PropTypes.func, toggleVisible: PropTypes.func, visible: PropTypes.bool, + uiRestrictions: PropTypes.object, }; const GaugeSeries = injectI18n(GaugeSeriesUi); diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/markdown/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/markdown/series.js index 015779857208..1f103ac6f0ae 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/markdown/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/markdown/series.js @@ -39,7 +39,8 @@ function MarkdownSeriesUi(props) { disableAdd, selectedTab, visible, - intl + intl, + uiRestrictions } = props; const defaults = { label: '', var_name: '' }; @@ -76,6 +77,7 @@ function MarkdownSeriesUi(props) { fields={fields} panel={panel} model={model} + uiRestrictions={uiRestrictions} /> @@ -197,6 +199,7 @@ MarkdownSeriesUi.propTypes = { switchTab: PropTypes.func, toggleVisible: PropTypes.func, visible: PropTypes.bool, + uiRestrictions: PropTypes.object, }; const MarkdownSeries = injectI18n(MarkdownSeriesUi); diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/metric/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/metric/series.js index 1618d1f74a3c..0df8136a81c3 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/metric/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/metric/series.js @@ -41,7 +41,8 @@ function MetricSeriesUi(props) { disableAdd, selectedTab, visible, - intl + intl, + uiRestrictions } = props; const defaults = { label: '' }; @@ -78,6 +79,7 @@ function MetricSeriesUi(props) { fields={fields} panel={panel} model={model} + uiRestrictions={uiRestrictions} /> @@ -232,6 +234,7 @@ MetricSeriesUi.propTypes = { toggleVisible: PropTypes.func, visible: PropTypes.bool, togglePanelActivation: PropTypes.func, + uiRestrictions: PropTypes.object, }; const MetricSeries = injectI18n(MetricSeriesUi); diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/table/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/table/series.js index 2837177ee977..d253be8608eb 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/table/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/table/series.js @@ -202,6 +202,7 @@ TableSeries.propTypes = { toggleVisible: PropTypes.func, visible: PropTypes.bool, togglePanelActivation: PropTypes.func, + uiRestrictions: PropTypes.object, }; export default injectI18n(TableSeries); diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/series.js index 1ef073464756..35ec9e57f4d0 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/timeseries/series.js @@ -41,7 +41,8 @@ const TimeseriesSeries = injectI18n(function (props) { selectedTab, onChange, visible, - intl + intl, + uiRestrictions } = props; const defaults = { label: '' }; @@ -78,6 +79,7 @@ const TimeseriesSeries = injectI18n(function (props) { fields={fields} panel={panel} model={model} + uiRestrictions={uiRestrictions} /> @@ -236,6 +238,7 @@ TimeseriesSeries.propTypes = { toggleVisible: PropTypes.func, visible: PropTypes.bool, togglePanelActivation: PropTypes.func, + uiRestrictions: PropTypes.object, }; export default injectI18n(TimeseriesSeries); diff --git a/src/legacy/core_plugins/metrics/public/components/vis_types/top_n/series.js b/src/legacy/core_plugins/metrics/public/components/vis_types/top_n/series.js index ba25d4261f5f..8d095a024b0d 100644 --- a/src/legacy/core_plugins/metrics/public/components/vis_types/top_n/series.js +++ b/src/legacy/core_plugins/metrics/public/components/vis_types/top_n/series.js @@ -42,7 +42,8 @@ const TopNSeries = injectI18n(function (props) { disableAdd, selectedTab, visible, - intl + intl, + uiRestrictions, } = props; const handleChange = createTextHandler(onChange); @@ -55,7 +56,7 @@ const TopNSeries = injectI18n(function (props) { if (visible) { let seriesBody; if (selectedTab === 'metrics') { - const handleSort = (data) => { + const handleSort = data => { const metrics = data.map(id => model.metrics.find(m => m.id === id)); props.onChange({ metrics }); }; @@ -76,6 +77,7 @@ const TopNSeries = injectI18n(function (props) { fields={fields} panel={panel} model={model} + uiRestrictions={uiRestrictions} /> @@ -226,6 +228,7 @@ TopNSeries.propTypes = { toggleVisible: PropTypes.func, visible: PropTypes.bool, togglePanelActivation: PropTypes.func, + uiRestrictions: PropTypes.object, }; export default TopNSeries; diff --git a/src/legacy/core_plugins/metrics/public/lib/check_ui_restrictions.js b/src/legacy/core_plugins/metrics/public/lib/check_ui_restrictions.js new file mode 100644 index 000000000000..825d7c5022fe --- /dev/null +++ b/src/legacy/core_plugins/metrics/public/lib/check_ui_restrictions.js @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { get } from 'lodash'; + +export const DEFAULT_UI_RESTRICTION = { + '*': true, +}; + +const RESTRICTION_TYPES = { + WHITE_LISTED_GROUP_BY_FIELDS: 'whiteListedGroupByFields', + WHITE_LISTER_METRICS: 'whiteListedMetrics', +}; + +const checkUIRestrictions = (key, restrictions = DEFAULT_UI_RESTRICTION, type) => { + const isAllEnabled = get(restrictions, `${type}.*`, true); + + return isAllEnabled || get(restrictions, type, {})[key]; +}; + +export const isMetricEnabled = (key, restrictions) => { + return checkUIRestrictions(key, restrictions, RESTRICTION_TYPES.WHITE_LISTER_METRICS); +}; + +export const isFieldEnabled = (field, metricType, restrictions = DEFAULT_UI_RESTRICTION) => { + if (isMetricEnabled(metricType, restrictions)) { + return checkUIRestrictions(field, restrictions[RESTRICTION_TYPES.WHITE_LISTER_METRICS], metricType); + } + return false; +}; + +export const isGroupByFieldsEnabled = (key, restrictions) => { + return checkUIRestrictions(key, restrictions, RESTRICTION_TYPES.WHITE_LISTED_GROUP_BY_FIELDS); +}; diff --git a/src/legacy/core_plugins/metrics/public/lib/extract_index_patterns.js b/src/legacy/core_plugins/metrics/public/lib/extract_index_patterns.js index 6aedc9ebe2c0..4b9b8cfa5d21 100644 --- a/src/legacy/core_plugins/metrics/public/lib/extract_index_patterns.js +++ b/src/legacy/core_plugins/metrics/public/lib/extract_index_patterns.js @@ -18,7 +18,7 @@ */ import { uniq } from 'lodash'; -export function extractIndexPatterns(params, fetchedFields) { +export function extractIndexPatterns(params, fetchedFields = {}) { const patternsToFetch = []; if (!fetchedFields[params.index_pattern]) { diff --git a/src/legacy/core_plugins/metrics/server/lib/search_strategies/default_search_capabilities.js b/src/legacy/core_plugins/metrics/server/lib/search_strategies/default_search_capabilities.js index 33e268577bf0..0d82cf7cb253 100644 --- a/src/legacy/core_plugins/metrics/server/lib/search_strategies/default_search_capabilities.js +++ b/src/legacy/core_plugins/metrics/server/lib/search_strategies/default_search_capabilities.js @@ -33,10 +33,32 @@ export class DefaultSearchCapabilities { return null; } + get whiteListedMetrics() { + return this.createUiRestriction(); + } + + get whiteListedGroupByFields() { + return this.createUiRestriction(); + } + + get uiRestrictions() { + return { + whiteListedMetrics: this.whiteListedMetrics, + whiteListedGroupByFields: this.whiteListedGroupByFields, + }; + } + get searchTimezone() { return getTimezoneFromRequest(this.request); } + createUiRestriction(restrictionsObject) { + return { + '*': !restrictionsObject, + ...(restrictionsObject || {}), + }; + } + parseInterval(interval) { return parseInterval(interval); } diff --git a/src/legacy/core_plugins/metrics/server/lib/vis_data/get_series_data.js b/src/legacy/core_plugins/metrics/server/lib/vis_data/get_series_data.js index 7a4a580481ac..16363684acc3 100644 --- a/src/legacy/core_plugins/metrics/server/lib/vis_data/get_series_data.js +++ b/src/legacy/core_plugins/metrics/server/lib/vis_data/get_series_data.js @@ -36,27 +36,37 @@ export async function getSeriesData(req, panel) { const body = (await Promise.all(bodiesPromises)) .reduce((acc, items) => acc.concat(items), []); - return searchRequest.search({ body }) - .then(data => { - const series = data.map(handleResponseBody(panel)); - return { - [panel.id]: { - id: panel.id, - series: series.reduce((acc, series) => acc.concat(series), []), - }, - }; - }) - .then(resp => { - if (!panel.annotations || panel.annotations.length === 0) return resp; - return getAnnotations(req, panel, esQueryConfig, searchStrategy, capabilities).then(annotations => { - resp[panel.id].annotations = annotations; - return resp; - }); - }) - .then(resp => { - resp.type = panel.type; - return resp; - }) - .catch(handleErrorResponse(panel)); -} + const meta = { + type: panel.type, + uiRestrictions: capabilities.uiRestrictions, + }; + try { + const data = await searchRequest.search({ body }); + const series = data.map(handleResponseBody(panel)); + let annotations = null; + + if (panel.annotations && panel.annotations.length) { + annotations = await getAnnotations(req, panel, esQueryConfig, searchStrategy, capabilities); + } + + return { + ...meta, + [panel.id]: { + annotations, + id: panel.id, + series: series.reduce((acc, series) => acc.concat(series), []), + }, + }; + + } catch (err) { + if (err.body) { + err.response = err.body; + + return { + ...meta, + ...handleErrorResponse(panel)(err), + }; + } + } +} diff --git a/src/legacy/core_plugins/metrics/server/lib/vis_data/get_table_data.js b/src/legacy/core_plugins/metrics/server/lib/vis_data/get_table_data.js index c552022473e0..1de391343516 100644 --- a/src/legacy/core_plugins/metrics/server/lib/vis_data/get_table_data.js +++ b/src/legacy/core_plugins/metrics/server/lib/vis_data/get_table_data.js @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import buildRequestBody from './table/build_request_body'; import handleErrorResponse from './handle_error_response'; import { get } from 'lodash'; @@ -33,14 +32,27 @@ export async function getTableData(req, panel) { const { indexPatternObject } = await getIndexPatternObject(req, panelIndexPattern); const body = buildRequestBody(req, panel, esQueryConfig, indexPatternObject, capabilities); + const meta = { + type: panel.type, + uiRestrictions: capabilities.uiRestrictions, + }; + try { const [resp] = await searchRequest.search({ body }); const buckets = get(resp, 'aggregations.pivot.buckets', []); - return { type: 'table', series: buckets.map(processBucket(panel)) }; + + return { + ...meta, + series: buckets.map(processBucket(panel)), + }; } catch (err) { if (err.body) { err.response = err.body; - return { type: 'table', ...handleErrorResponse(panel)(err) }; + + return { + ...meta, + ...handleErrorResponse(panel)(err) + }; } } } diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index 2eb0cdc0c4f5..569cf55b532d 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -64,7 +64,10 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { return Promise.all(requests) .then(results => { return results.map(result => { - const metricIds = Object.keys(result).filter(k => k !== 'type'); + const metricIds = Object.keys(result).filter( + k => !['type', 'uiRestrictions'].includes(k) + ); + return metricIds.map((id: string) => { const infraMetricId: InfraMetric = (InfraMetric as any)[id]; if (!infraMetricId) { diff --git a/x-pack/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js b/x-pack/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js index 3e26b5c9b853..d8d54ef5a423 100644 --- a/x-pack/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js +++ b/x-pack/plugins/rollup/server/lib/search_strategies/rollup_search_capabilities.js @@ -3,18 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +import { get, has } from 'lodash'; import { unitsMap } from '@elastic/datemath'; const leastCommonInterval = (num = 0, base = 0) => Math.max(Math.ceil(num / base) * base, base); - -const getDateHistogramAggregation = (fieldsCapabilities, rollupIndex) => { - const dateHistogramField = fieldsCapabilities[rollupIndex].aggs.date_histogram; - - // there is also only one valid date_histogram field - return Object.values(dateHistogramField)[0]; -}; - const isCalendarInterval = ({ unit, value }) => value === 1 && ['calendar', 'mixed'].includes(unitsMap[unit].type); export const getRollupSearchCapabilities = (DefaultSearchCapabilities) => @@ -23,7 +15,13 @@ export const getRollupSearchCapabilities = (DefaultSearchCapabilities) => super(req, batchRequestsSupport, fieldsCapabilities); this.rollupIndex = rollupIndex; - this.dateHistogram = getDateHistogramAggregation(fieldsCapabilities, rollupIndex); + this.availableMetrics = get(fieldsCapabilities, `${rollupIndex}.aggs`, {}); + } + + get dateHistogram() { + const [dateHistogram] = Object.values(this.availableMetrics.date_histogram); + + return dateHistogram; } get defaultTimeInterval() { @@ -34,6 +32,30 @@ export const getRollupSearchCapabilities = (DefaultSearchCapabilities) => return get(this.dateHistogram, 'time_zone', null); } + get whiteListedMetrics() { + const baseRestrictions = this.createUiRestriction({ + count: this.createUiRestriction(), + }); + + const getFields = fields => Object.keys(fields) + .reduce((acc, item) => ({ + ...acc, + [item]: true, + }), this.createUiRestriction({})); + + return Object.keys(this.availableMetrics).reduce((acc, item) => ({ + ...acc, + [item]: getFields(this.availableMetrics[item]), + }), baseRestrictions); + } + + get whiteListedGroupByFields() { + return this.createUiRestriction({ + everything: true, + terms: has(this.availableMetrics, 'terms'), + }); + } + getValidTimeInterval(userIntervalString) { const parsedRollupJobInterval = this.parseInterval(this.defaultTimeInterval); const parsedUserInterval = this.parseInterval(userIntervalString);