[TSVB] [Rollup support] exclude unsupported metrics from the Metric Aggregations dropdown (#34165)
* [TSVB] [Rollup support] exclude unsupported metrics from the Metric Aggregations dropdown Fix: #34047 * TSVB] [Rollup support] exclude unsupported metrics from the Metric Aggregations dropdown - fix broken tests * [TSVB] [Rollup support] exclude unsupported metrics from the Metric Aggregations dropdown * [TSVB] [Rollup support] exclude unsupported metrics from the Metric Aggregations dropdown - add filtering for group by * fix tests * Fix issue with fetching fields
This commit is contained in:
parent
aff8c69deb
commit
3099837678
|
@ -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 (
|
||||
<div
|
||||
className={props.className}
|
||||
|
@ -50,6 +59,7 @@ function Agg(props) {
|
|||
panel={props.panel}
|
||||
series={props.series}
|
||||
siblings={props.siblings}
|
||||
uiRestrictions={props.uiRestrictions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -70,6 +80,7 @@ Agg.propTypes = {
|
|||
series: PropTypes.object,
|
||||
siblings: PropTypes.array,
|
||||
sortData: PropTypes.string,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default sortable(Agg);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (
|
||||
<EuiComboBox
|
||||
placeholder={intl.formatMessage({ id: 'tsvb.fieldSelect.selectFieldPlaceholder', defaultMessage: 'Select field…' })}
|
||||
placeholder={intl.formatMessage({
|
||||
id: 'tsvb.fieldSelect.selectFieldPlaceholder',
|
||||
defaultMessage: 'Select field...',
|
||||
})}
|
||||
isDisabled={disabled}
|
||||
options={options}
|
||||
selectedOptions={selectedOptions}
|
||||
|
@ -57,7 +70,7 @@ function FieldSelectUi(props) {
|
|||
FieldSelectUi.defaultProps = {
|
||||
indexPattern: '*',
|
||||
disabled: false,
|
||||
restrict: 'none'
|
||||
restrict: 'none',
|
||||
};
|
||||
|
||||
FieldSelectUi.propTypes = {
|
||||
|
@ -68,7 +81,8 @@ FieldSelectUi.propTypes = {
|
|||
onChange: PropTypes.func,
|
||||
restrict: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
value: PropTypes.string
|
||||
value: PropTypes.string,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
const FieldSelect = injectI18n(FieldSelectUi);
|
||||
|
|
|
@ -28,8 +28,7 @@ import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFormLabel }
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
function StandardAgg(props) {
|
||||
const { model, panel, series, fields } = props;
|
||||
|
||||
const { model, panel, series, fields, uiRestrictions } = props;
|
||||
const handleChange = createChangeHandler(props.onChange, model);
|
||||
const handleSelectChange = createSelectHandler(handleChange);
|
||||
let restrict = 'numeric';
|
||||
|
@ -61,6 +60,7 @@ function StandardAgg(props) {
|
|||
panelType={props.panel.type}
|
||||
siblings={props.siblings}
|
||||
value={model.type}
|
||||
uiRestrictions={uiRestrictions}
|
||||
onChange={handleSelectChange('type')}
|
||||
fullWidth
|
||||
/>
|
||||
|
@ -85,6 +85,7 @@ function StandardAgg(props) {
|
|||
indexPattern={indexPattern}
|
||||
value={model.field}
|
||||
onChange={handleSelectChange('field')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
fullWidth
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
@ -95,7 +96,6 @@ function StandardAgg(props) {
|
|||
</EuiFlexGroup>
|
||||
</AggRow>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
StandardAgg.propTypes = {
|
||||
|
@ -108,6 +108,7 @@ StandardAgg.propTypes = {
|
|||
panel: PropTypes.object,
|
||||
series: PropTypes.object,
|
||||
siblings: PropTypes.array,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default StandardAgg;
|
||||
|
|
|
@ -128,6 +128,7 @@ const StandardSiblingAggUi = props => {
|
|||
panelType={props.panel.type}
|
||||
siblings={props.siblings}
|
||||
value={model.type}
|
||||
uiRestrictions={props.uiRestrictions}
|
||||
onChange={handleSelectChange('type')}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
@ -165,6 +166,7 @@ StandardSiblingAggUi.propTypes = {
|
|||
panel: PropTypes.object,
|
||||
series: PropTypes.object,
|
||||
siblings: PropTypes.array,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export const StandardSiblingAgg = injectI18n(StandardSiblingAggUi);
|
||||
|
|
|
@ -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 (
|
||||
<AggRow
|
||||
disableDelete={props.disableDelete}
|
||||
model={props.model}
|
||||
onAdd={props.onAdd}
|
||||
onDelete={props.onDelete}
|
||||
siblings={props.siblings}
|
||||
>
|
||||
<EuiTitle className="tvbAggRow__unavailable" size="xxxs">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="tsvb.unsupportedAgg.aggIsTemporaryUnsupportedDescription"
|
||||
defaultMessage="The {modelType} aggregation is currently unsupported."
|
||||
values={{ modelType: (<EuiCode>{props.model.type}</EuiCode>) }}
|
||||
/>
|
||||
</span>
|
||||
</EuiTitle>
|
||||
</AggRow>
|
||||
);
|
||||
}
|
|
@ -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 (
|
||||
<Agg
|
||||
key={row.id}
|
||||
|
@ -39,6 +40,7 @@ export default function createAggRowRender(props) {
|
|||
panel={panel}
|
||||
series={model}
|
||||
siblings={items}
|
||||
uiRestrictions={uiRestrictions}
|
||||
sortData={row.id}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
|
@ -280,6 +281,7 @@ TablePanelConfig.propTypes = {
|
|||
fields: PropTypes.object,
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
visData$: PropTypes.object,
|
||||
};
|
||||
|
||||
export default TablePanelConfig;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 (
|
||||
<SplitByFilter
|
||||
model={model}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (model.split_mode === 'filters') {
|
||||
return (
|
||||
<SplitByFilters
|
||||
model={model}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (model.split_mode === 'terms') {
|
||||
return (
|
||||
<SplitByTerms
|
||||
model={model}
|
||||
indexPattern={indexPattern}
|
||||
fields={this.props.fields}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<SplitByEverything
|
||||
model={model}
|
||||
onChange={this.props.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const splitMode = get(this.props, 'model.split_mode', SPLIT_MODES.EVERYTHING);
|
||||
|
||||
const Component = this.getComponent(splitMode, uiRestrictions);
|
||||
|
||||
return (
|
||||
<Component
|
||||
model={model}
|
||||
indexPattern={indexPattern}
|
||||
fields={this.props.fields}
|
||||
onChange={this.props.onChange}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
Split.propTypes = {
|
||||
fields: PropTypes.object,
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
panel: PropTypes.object
|
||||
panel: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Split;
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
|
|
@ -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) {
|
|||
<GroupBySelect
|
||||
value={model.split_mode}
|
||||
onChange={handleSelectChange('split_mode')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
SplitByEverything.propTypes = {
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func
|
||||
onChange: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SplitByEverything;
|
||||
|
||||
|
|
|
@ -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 => {
|
|||
<GroupBySelect
|
||||
value={model.split_mode}
|
||||
onChange={handleSelectChange('split_mode')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -68,5 +69,6 @@ export const SplitByFilter = props => {
|
|||
|
||||
SplitByFilter.propTypes = {
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func
|
||||
onChange: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
|
|
@ -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 (
|
||||
<div>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem>
|
||||
|
@ -43,6 +43,7 @@ function SplitByFilters(props) {
|
|||
<GroupBySelect
|
||||
value={model.split_mode}
|
||||
onChange={handleSelectChange('split_mode')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -55,11 +56,10 @@ function SplitByFilters(props) {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
SplitByFilters.propTypes = {
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func
|
||||
onChange: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SplitByFilters;
|
||||
|
|
|
@ -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 (
|
||||
<EuiComboBox
|
||||
id={props.id}
|
||||
|
@ -58,13 +70,12 @@ function GroupBySelectUi(props) {
|
|||
singleSelection={{ asPlainText: true }}
|
||||
/>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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
|
|||
<GroupBySelect
|
||||
value={model.split_mode}
|
||||
onChange={handleSelectChange('split_mode')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -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'}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
|
@ -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);
|
||||
|
|
|
@ -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 (
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<EuiFormRow
|
||||
id={htmlId('group')}
|
||||
label={(<FormattedMessage
|
||||
id="tsvb.splits.everything.groupByLabel"
|
||||
defaultMessage="Group by"
|
||||
/>)}
|
||||
>
|
||||
<GroupBySelect
|
||||
value={model.split_mode}
|
||||
onChange={handleSelectChange('split_mode')}
|
||||
uiRestrictions={uiRestrictions}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle className="tvbAggRow__unavailable" size="xxxs">
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id="tsvb.unsupportedSplit.splitIsUnsupportedDescription"
|
||||
defaultMessage="Split by {modelType} is unsupported."
|
||||
values={{ modelType: (<EuiCode>{model.split_mode}</EuiCode>) }}
|
||||
/>
|
||||
</span>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
SplitUnsupported.propTypes = {
|
||||
model: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -227,6 +229,7 @@ GaugeSeriesUi.propTypes = {
|
|||
switchTab: PropTypes.func,
|
||||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
const GaugeSeries = injectI18n(GaugeSeriesUi);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -197,6 +199,7 @@ MarkdownSeriesUi.propTypes = {
|
|||
switchTab: PropTypes.func,
|
||||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
const MarkdownSeries = injectI18n(MarkdownSeriesUi);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -232,6 +234,7 @@ MetricSeriesUi.propTypes = {
|
|||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
togglePanelActivation: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
const MetricSeries = injectI18n(MetricSeriesUi);
|
||||
|
|
|
@ -202,6 +202,7 @@ TableSeries.propTypes = {
|
|||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
togglePanelActivation: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default injectI18n(TableSeries);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -236,6 +238,7 @@ TimeseriesSeries.propTypes = {
|
|||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
togglePanelActivation: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default injectI18n(TimeseriesSeries);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -226,6 +228,7 @@ TopNSeries.propTypes = {
|
|||
toggleVisible: PropTypes.func,
|
||||
visible: PropTypes.bool,
|
||||
togglePanelActivation: PropTypes.func,
|
||||
uiRestrictions: PropTypes.object,
|
||||
};
|
||||
|
||||
export default TopNSeries;
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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]) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue