[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 aggToComponent from '../lib/agg_to_component';
|
||||||
import { sortable } from 'react-anything-sortable';
|
import { sortable } from 'react-anything-sortable';
|
||||||
import { UnsupportedAgg } from './unsupported_agg';
|
import { UnsupportedAgg } from './unsupported_agg';
|
||||||
|
import { TemporaryUnsupportedAgg } from './temporary_unsupported_agg';
|
||||||
|
|
||||||
|
import { isMetricEnabled } from '../../lib/check_ui_restrictions';
|
||||||
|
|
||||||
function Agg(props) {
|
function Agg(props) {
|
||||||
const { model } = props;
|
const { model, uiRestrictions } = props;
|
||||||
|
|
||||||
let Component = aggToComponent[model.type];
|
let Component = aggToComponent[model.type];
|
||||||
|
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
Component = UnsupportedAgg;
|
Component = UnsupportedAgg;
|
||||||
|
} else if (!isMetricEnabled(model.type, uiRestrictions)) {
|
||||||
|
Component = TemporaryUnsupportedAgg;
|
||||||
}
|
}
|
||||||
|
|
||||||
const style = {
|
const style = {
|
||||||
cursor: 'default',
|
cursor: 'default',
|
||||||
...props.style,
|
...props.style,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={props.className}
|
className={props.className}
|
||||||
|
@ -50,6 +59,7 @@ function Agg(props) {
|
||||||
panel={props.panel}
|
panel={props.panel}
|
||||||
series={props.series}
|
series={props.series}
|
||||||
siblings={props.siblings}
|
siblings={props.siblings}
|
||||||
|
uiRestrictions={props.uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -70,6 +80,7 @@ Agg.propTypes = {
|
||||||
series: PropTypes.object,
|
series: PropTypes.object,
|
||||||
siblings: PropTypes.array,
|
siblings: PropTypes.array,
|
||||||
sortData: PropTypes.string,
|
sortData: PropTypes.string,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sortable(Agg);
|
export default sortable(Agg);
|
||||||
|
|
|
@ -22,6 +22,7 @@ import React from 'react';
|
||||||
import { EuiComboBox } from '@elastic/eui';
|
import { EuiComboBox } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { injectI18n } from '@kbn/i18n/react';
|
import { injectI18n } from '@kbn/i18n/react';
|
||||||
|
import { isMetricEnabled } from '../../lib/check_ui_restrictions';
|
||||||
|
|
||||||
const metricAggs = [
|
const metricAggs = [
|
||||||
{
|
{
|
||||||
|
@ -169,41 +170,45 @@ function filterByPanelType(panelType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function AggSelectUi(props) {
|
function AggSelectUi(props) {
|
||||||
const { siblings, panelType, value, onChange, intl, ...rest } = props;
|
const { siblings, panelType, value, onChange, intl, uiRestrictions, ...rest } = props;
|
||||||
const selectedOption = allAggOptions.find(option => {
|
|
||||||
return value === option.value;
|
const selectedOptions = allAggOptions.filter(option => {
|
||||||
|
return value === option.value && isMetricEnabled(option.value, uiRestrictions);
|
||||||
});
|
});
|
||||||
const selectedOptions = selectedOption ? [selectedOption] : [];
|
|
||||||
|
|
||||||
let enablePipelines = siblings.some(
|
let enablePipelines = siblings.some(
|
||||||
s => !!metricAggs.find(m => m.value === s.type)
|
s => !!metricAggs.find(m => m.value === s.type)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (siblings.length <= 1) enablePipelines = false;
|
if (siblings.length <= 1) enablePipelines = false;
|
||||||
|
|
||||||
let options;
|
let options;
|
||||||
if (panelType === 'metrics') {
|
if (panelType === 'metrics') {
|
||||||
options = metricAggs;
|
options = metricAggs;
|
||||||
} else {
|
} else {
|
||||||
|
const disableSiblingAggs = agg => ({ ...agg, disabled: !enablePipelines || !isMetricEnabled(agg.value, uiRestrictions) });
|
||||||
|
|
||||||
options = [
|
options = [
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({ id: 'tsvb.aggSelect.aggGroups.metricAggLabel', defaultMessage: 'Metric Aggregations' }),
|
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({
|
label: intl.formatMessage({
|
||||||
id: 'tsvb.aggSelect.aggGroups.parentPipelineAggLabel', defaultMessage: 'Parent Pipeline Aggregations' }),
|
id: 'tsvb.aggSelect.aggGroups.parentPipelineAggLabel', defaultMessage: 'Parent Pipeline Aggregations' }),
|
||||||
options: pipelineAggs
|
options: pipelineAggs
|
||||||
.filter(filterByPanelType(panelType))
|
.filter(filterByPanelType(panelType))
|
||||||
.map(agg => ({ ...agg, disabled: !enablePipelines })),
|
.map(disableSiblingAggs),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({
|
label: intl.formatMessage({
|
||||||
id: 'tsvb.aggSelect.aggGroups.siblingPipelineAggLabel', defaultMessage: 'Sibling Pipeline Aggregations' }),
|
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' }),
|
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,
|
panelType: PropTypes.string,
|
||||||
siblings: PropTypes.array,
|
siblings: PropTypes.array,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const AggSelect = injectI18n(AggSelectUi);
|
const AggSelect = injectI18n(AggSelectUi);
|
||||||
|
|
|
@ -24,26 +24,39 @@ import {
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import generateByTypeFilter from '../lib/generate_by_type_filter';
|
import generateByTypeFilter from '../lib/generate_by_type_filter';
|
||||||
import { injectI18n } from '@kbn/i18n/react';
|
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') {
|
if (type === 'count') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const options = (fields[indexPattern] || [])
|
|
||||||
.filter(generateByTypeFilter(restrict))
|
|
||||||
.map(field => {
|
|
||||||
return { label: field.name, value: field.name };
|
|
||||||
});
|
|
||||||
|
|
||||||
const selectedOption = options.find(option => {
|
const typeFilter = generateByTypeFilter(restrict);
|
||||||
return value === option.value;
|
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] : [];
|
const selectedOptions = selectedOption ? [selectedOption] : [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiComboBox
|
<EuiComboBox
|
||||||
placeholder={intl.formatMessage({ id: 'tsvb.fieldSelect.selectFieldPlaceholder', defaultMessage: 'Select field…' })}
|
placeholder={intl.formatMessage({
|
||||||
|
id: 'tsvb.fieldSelect.selectFieldPlaceholder',
|
||||||
|
defaultMessage: 'Select field...',
|
||||||
|
})}
|
||||||
isDisabled={disabled}
|
isDisabled={disabled}
|
||||||
options={options}
|
options={options}
|
||||||
selectedOptions={selectedOptions}
|
selectedOptions={selectedOptions}
|
||||||
|
@ -57,7 +70,7 @@ function FieldSelectUi(props) {
|
||||||
FieldSelectUi.defaultProps = {
|
FieldSelectUi.defaultProps = {
|
||||||
indexPattern: '*',
|
indexPattern: '*',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
restrict: 'none'
|
restrict: 'none',
|
||||||
};
|
};
|
||||||
|
|
||||||
FieldSelectUi.propTypes = {
|
FieldSelectUi.propTypes = {
|
||||||
|
@ -68,7 +81,8 @@ FieldSelectUi.propTypes = {
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
restrict: PropTypes.string,
|
restrict: PropTypes.string,
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
value: PropTypes.string
|
value: PropTypes.string,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FieldSelect = injectI18n(FieldSelectUi);
|
const FieldSelect = injectI18n(FieldSelectUi);
|
||||||
|
|
|
@ -28,8 +28,7 @@ import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFormLabel }
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
function StandardAgg(props) {
|
function StandardAgg(props) {
|
||||||
const { model, panel, series, fields } = props;
|
const { model, panel, series, fields, uiRestrictions } = props;
|
||||||
|
|
||||||
const handleChange = createChangeHandler(props.onChange, model);
|
const handleChange = createChangeHandler(props.onChange, model);
|
||||||
const handleSelectChange = createSelectHandler(handleChange);
|
const handleSelectChange = createSelectHandler(handleChange);
|
||||||
let restrict = 'numeric';
|
let restrict = 'numeric';
|
||||||
|
@ -61,6 +60,7 @@ function StandardAgg(props) {
|
||||||
panelType={props.panel.type}
|
panelType={props.panel.type}
|
||||||
siblings={props.siblings}
|
siblings={props.siblings}
|
||||||
value={model.type}
|
value={model.type}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
onChange={handleSelectChange('type')}
|
onChange={handleSelectChange('type')}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
@ -85,6 +85,7 @@ function StandardAgg(props) {
|
||||||
indexPattern={indexPattern}
|
indexPattern={indexPattern}
|
||||||
value={model.field}
|
value={model.field}
|
||||||
onChange={handleSelectChange('field')}
|
onChange={handleSelectChange('field')}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
|
@ -95,7 +96,6 @@ function StandardAgg(props) {
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</AggRow>
|
</AggRow>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StandardAgg.propTypes = {
|
StandardAgg.propTypes = {
|
||||||
|
@ -108,6 +108,7 @@ StandardAgg.propTypes = {
|
||||||
panel: PropTypes.object,
|
panel: PropTypes.object,
|
||||||
series: PropTypes.object,
|
series: PropTypes.object,
|
||||||
siblings: PropTypes.array,
|
siblings: PropTypes.array,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StandardAgg;
|
export default StandardAgg;
|
||||||
|
|
|
@ -128,6 +128,7 @@ const StandardSiblingAggUi = props => {
|
||||||
panelType={props.panel.type}
|
panelType={props.panel.type}
|
||||||
siblings={props.siblings}
|
siblings={props.siblings}
|
||||||
value={model.type}
|
value={model.type}
|
||||||
|
uiRestrictions={props.uiRestrictions}
|
||||||
onChange={handleSelectChange('type')}
|
onChange={handleSelectChange('type')}
|
||||||
/>
|
/>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
@ -165,6 +166,7 @@ StandardSiblingAggUi.propTypes = {
|
||||||
panel: PropTypes.object,
|
panel: PropTypes.object,
|
||||||
series: PropTypes.object,
|
series: PropTypes.object,
|
||||||
siblings: PropTypes.array,
|
siblings: PropTypes.array,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StandardSiblingAgg = injectI18n(StandardSiblingAggUi);
|
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) {
|
export default function createAggRowRender(props) {
|
||||||
return (row, index, items) => {
|
return (row, index, items) => {
|
||||||
const { panel, model, fields } = props;
|
const { panel, model, fields, uiRestrictions } = props;
|
||||||
const changeHandler = seriesChangeHandler(props, items);
|
const changeHandler = seriesChangeHandler(props, items);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Agg
|
<Agg
|
||||||
key={row.id}
|
key={row.id}
|
||||||
|
@ -39,6 +40,7 @@ export default function createAggRowRender(props) {
|
||||||
panel={panel}
|
panel={panel}
|
||||||
series={model}
|
series={model}
|
||||||
siblings={items}
|
siblings={items}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
sortData={row.id}
|
sortData={row.id}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -107,6 +107,7 @@ class GaugePanelConfigUi extends Component {
|
||||||
limit={1}
|
limit={1}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -340,6 +341,7 @@ GaugePanelConfigUi.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GaugePanelConfig = injectI18n(GaugePanelConfigUi);
|
const GaugePanelConfig = injectI18n(GaugePanelConfigUi);
|
||||||
|
|
|
@ -110,6 +110,7 @@ class MarkdownPanelConfigUi extends Component {
|
||||||
fields={this.props.fields}
|
fields={this.props.fields}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -315,7 +316,8 @@ MarkdownPanelConfigUi.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
dateFormat: PropTypes.string
|
dateFormat: PropTypes.string,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MarkdownPanelConfig = injectI18n(MarkdownPanelConfigUi);
|
const MarkdownPanelConfig = injectI18n(MarkdownPanelConfigUi);
|
||||||
|
|
|
@ -76,6 +76,7 @@ class MetricPanelConfig extends Component {
|
||||||
limit={2}
|
limit={2}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -191,6 +192,7 @@ MetricPanelConfig.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MetricPanelConfig;
|
export default MetricPanelConfig;
|
||||||
|
|
|
@ -161,6 +161,7 @@ class TablePanelConfig extends Component {
|
||||||
fields={this.props.fields}
|
fields={this.props.fields}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -280,6 +281,7 @@ TablePanelConfig.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TablePanelConfig;
|
export default TablePanelConfig;
|
||||||
|
|
|
@ -119,6 +119,7 @@ class TimeseriesPanelConfigUi extends Component {
|
||||||
fields={this.props.fields}
|
fields={this.props.fields}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -379,6 +380,7 @@ TimeseriesPanelConfigUi.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const TimeseriesPanelConfig = injectI18n(TimeseriesPanelConfigUi);
|
const TimeseriesPanelConfig = injectI18n(TimeseriesPanelConfigUi);
|
||||||
|
|
|
@ -77,6 +77,7 @@ class TopNPanelConfig extends Component {
|
||||||
fields={this.props.fields}
|
fields={this.props.fields}
|
||||||
model={this.props.model}
|
model={this.props.model}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
|
visData$={this.props.visData$}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -246,6 +247,7 @@ TopNPanelConfig.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TopNPanelConfig;
|
export default TopNPanelConfig;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import _ from 'lodash';
|
import { assign, get } from 'lodash';
|
||||||
|
|
||||||
import timeseries from './vis_types/timeseries/series';
|
import timeseries from './vis_types/timeseries/series';
|
||||||
import metric from './vis_types/metric/series';
|
import metric from './vis_types/metric/series';
|
||||||
|
@ -46,7 +46,10 @@ class Series extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
visible: true,
|
visible: true,
|
||||||
selectedTab: 'metrics',
|
selectedTab: 'metrics',
|
||||||
|
uiRestrictions: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.visDataSubscription = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
switchTab = (selectedTab) => {
|
switchTab = (selectedTab) => {
|
||||||
|
@ -56,7 +59,7 @@ class Series extends Component {
|
||||||
handleChange = (part) => {
|
handleChange = (part) => {
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
const { model } = this.props;
|
const { model } = this.props;
|
||||||
const doc = _.assign({}, model, part);
|
const doc = assign({}, model, part);
|
||||||
this.props.onChange(doc);
|
this.props.onChange(doc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -71,12 +74,25 @@ class Series extends Component {
|
||||||
|
|
||||||
toggleVisible = (e) => {
|
toggleVisible = (e) => {
|
||||||
e.preventDefault();
|
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() {
|
render() {
|
||||||
const { panel } = this.props;
|
const { panel } = this.props;
|
||||||
const Component = lookup[panel.type];
|
const Component = lookup[panel.type];
|
||||||
|
|
||||||
if (Component) {
|
if (Component) {
|
||||||
const params = {
|
const params = {
|
||||||
className: this.props.className,
|
className: this.props.className,
|
||||||
|
@ -98,6 +114,7 @@ class Series extends Component {
|
||||||
selectedTab: this.state.selectedTab,
|
selectedTab: this.state.selectedTab,
|
||||||
sortData: this.props.sortData,
|
sortData: this.props.sortData,
|
||||||
style: this.props.style,
|
style: this.props.style,
|
||||||
|
uiRestrictions: this.state.uiRestrictions,
|
||||||
switchTab: this.switchTab,
|
switchTab: this.switchTab,
|
||||||
toggleVisible: this.toggleVisible,
|
toggleVisible: this.toggleVisible,
|
||||||
togglePanelActivation: this.togglePanelActivation,
|
togglePanelActivation: this.togglePanelActivation,
|
||||||
|
@ -116,6 +133,11 @@ class Series extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.visDataSubscription) {
|
||||||
|
this.visDataSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Series.defaultProps = {
|
Series.defaultProps = {
|
||||||
|
@ -139,6 +161,7 @@ Series.propTypes = {
|
||||||
onTouchStart: PropTypes.func,
|
onTouchStart: PropTypes.func,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
panel: PropTypes.object,
|
panel: PropTypes.object,
|
||||||
|
visData$: PropTypes.object,
|
||||||
sortData: PropTypes.string,
|
sortData: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ class SeriesEditor extends Component {
|
||||||
onClone={() => this.handleClone(row)}
|
onClone={() => this.handleClone(row)}
|
||||||
onDelete={handleDelete.bind(null, props, row)}
|
onDelete={handleDelete.bind(null, props, row)}
|
||||||
onShouldSortItem={(direction) => this.sortSeries(index, direction, allSeries)}
|
onShouldSortItem={(direction) => this.sortSeries(index, direction, allSeries)}
|
||||||
|
visData$={this.props.visData$}
|
||||||
model={row}
|
model={row}
|
||||||
panel={model}
|
panel={model}
|
||||||
sortData={row.id}
|
sortData={row.id}
|
||||||
|
@ -115,7 +116,8 @@ SeriesEditor.propTypes = {
|
||||||
limit: PropTypes.number,
|
limit: PropTypes.number,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func,
|
||||||
|
visData$: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SeriesEditor;
|
export default SeriesEditor;
|
||||||
|
|
|
@ -20,71 +20,77 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
import { SplitByTerms } from './splits/terms';
|
import { SplitByTerms } from './splits/terms';
|
||||||
import { SplitByFilter } from './splits/filter';
|
import { SplitByFilter } from './splits/filter';
|
||||||
import SplitByFilters from './splits/filters';
|
import { SplitByFilters } from './splits/filters';
|
||||||
import SplitByEverything from './splits/everything';
|
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 {
|
class Split extends Component {
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const { model } = nextProps;
|
const { model } = nextProps;
|
||||||
if (model.split_mode === 'filters' && !model.split_filters) {
|
if (model.split_mode === 'filters' && !model.split_filters) {
|
||||||
this.props.onChange({
|
this.props.onChange({
|
||||||
split_filters: [
|
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() {
|
render() {
|
||||||
const { model, panel } = this.props;
|
const { model, panel, uiRestrictions } = this.props;
|
||||||
const indexPattern = model.override_index_pattern &&
|
const indexPattern = model.override_index_pattern &&
|
||||||
model.series_index_pattern ||
|
model.series_index_pattern ||
|
||||||
panel.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 = {
|
Split.propTypes = {
|
||||||
fields: PropTypes.object,
|
fields: PropTypes.object,
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
panel: PropTypes.object
|
panel: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Split;
|
export default Split;
|
||||||
|
|
|
@ -55,6 +55,7 @@ exports[`src/legacy/core_plugins/metrics/public/components/splits/terms.test.js
|
||||||
}
|
}
|
||||||
indexPattern="kibana_sample_data_flights"
|
indexPattern="kibana_sample_data_flights"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
|
type="terms"
|
||||||
value="OriginCityName"
|
value="OriginCityName"
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
|
|
|
@ -18,14 +18,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import createSelectHandler from '../lib/create_select_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 PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
function SplitByEverything(props) {
|
export const SplitByEverything = (props) => {
|
||||||
const { onChange, model } = props;
|
const { onChange, model, uiRestrictions } = props;
|
||||||
const htmlId = htmlIdGenerator();
|
const htmlId = htmlIdGenerator();
|
||||||
const handleSelectChange = createSelectHandler(onChange);
|
const handleSelectChange = createSelectHandler(onChange);
|
||||||
return (
|
return (
|
||||||
|
@ -41,18 +41,18 @@ function SplitByEverything(props) {
|
||||||
<GroupBySelect
|
<GroupBySelect
|
||||||
value={model.split_mode}
|
value={model.split_mode}
|
||||||
onChange={handleSelectChange('split_mode')}
|
onChange={handleSelectChange('split_mode')}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|
||||||
SplitByEverything.propTypes = {
|
SplitByEverything.propTypes = {
|
||||||
model: PropTypes.object,
|
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 createTextHandler from '../lib/create_text_handler';
|
||||||
import createSelectHandler from '../lib/create_select_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 PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui';
|
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiFieldText } from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
export const SplitByFilter = props => {
|
export const SplitByFilter = props => {
|
||||||
const { onChange } = props;
|
const { onChange, uiRestrictions } = props;
|
||||||
const defaults = { filter: '' };
|
const defaults = { filter: '' };
|
||||||
const model = { ...defaults, ...props.model };
|
const model = { ...defaults, ...props.model };
|
||||||
const htmlId = htmlIdGenerator();
|
const htmlId = htmlIdGenerator();
|
||||||
|
@ -45,6 +45,7 @@ export const SplitByFilter = props => {
|
||||||
<GroupBySelect
|
<GroupBySelect
|
||||||
value={model.split_mode}
|
value={model.split_mode}
|
||||||
onChange={handleSelectChange('split_mode')}
|
onChange={handleSelectChange('split_mode')}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
@ -68,5 +69,6 @@ export const SplitByFilter = props => {
|
||||||
|
|
||||||
SplitByFilter.propTypes = {
|
SplitByFilter.propTypes = {
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,18 +18,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import createSelectHandler from '../lib/create_select_handler';
|
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 FilterItems from './filter_items';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, EuiFormRow } from '@elastic/eui';
|
||||||
import { FormattedMessage } from '@kbn/i18n/react';
|
import { FormattedMessage } from '@kbn/i18n/react';
|
||||||
|
|
||||||
function SplitByFilters(props) {
|
export const SplitByFilters = (props) => {
|
||||||
const { onChange, model } = props;
|
const { onChange, model, uiRestrictions } = props;
|
||||||
const htmlId = htmlIdGenerator();
|
const htmlId = htmlIdGenerator();
|
||||||
const handleSelectChange = createSelectHandler(onChange);
|
const handleSelectChange = createSelectHandler(onChange);
|
||||||
return(
|
return (
|
||||||
<div>
|
<div>
|
||||||
<EuiFlexGroup alignItems="center">
|
<EuiFlexGroup alignItems="center">
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
|
@ -43,6 +43,7 @@ function SplitByFilters(props) {
|
||||||
<GroupBySelect
|
<GroupBySelect
|
||||||
value={model.split_mode}
|
value={model.split_mode}
|
||||||
onChange={handleSelectChange('split_mode')}
|
onChange={handleSelectChange('split_mode')}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
@ -55,11 +56,10 @@ function SplitByFilters(props) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
SplitByFilters.propTypes = {
|
SplitByFilters.propTypes = {
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func
|
onChange: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SplitByFilters;
|
|
||||||
|
|
|
@ -23,31 +23,43 @@ import {
|
||||||
EuiComboBox,
|
EuiComboBox,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import { injectI18n } from '@kbn/i18n/react';
|
import { injectI18n } from '@kbn/i18n/react';
|
||||||
|
import { isGroupByFieldsEnabled } from '../../lib/check_ui_restrictions';
|
||||||
|
|
||||||
function GroupBySelectUi(props) {
|
function GroupBySelectUi(props) {
|
||||||
const { intl } = props;
|
const { intl, uiRestrictions } = props;
|
||||||
const modeOptions = [
|
const modeOptions = ([
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.everythingLabel', defaultMessage: 'Everything' }),
|
label: intl.formatMessage({
|
||||||
value: 'everything'
|
id: 'tsvb.splits.groupBySelect.modeOptions.everythingLabel',
|
||||||
|
defaultMessage: 'Everything',
|
||||||
|
}),
|
||||||
|
value: 'everything',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.filterLabel', defaultMessage: 'Filter' }),
|
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' }),
|
label: intl.formatMessage({
|
||||||
value: 'filters'
|
id: 'tsvb.splits.groupBySelect.modeOptions.filtersLabel',
|
||||||
|
defaultMessage: 'Filters',
|
||||||
|
}),
|
||||||
|
value: 'filters',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: intl.formatMessage({ id: 'tsvb.splits.groupBySelect.modeOptions.termsLabel', defaultMessage: 'Terms' }),
|
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 selectedValue = props.value || 'everything';
|
||||||
const selectedOption = modeOptions.find(option => {
|
const selectedOption = modeOptions.find(option => {
|
||||||
return selectedValue === option.value;
|
return selectedValue === option.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiComboBox
|
<EuiComboBox
|
||||||
id={props.id}
|
id={props.id}
|
||||||
|
@ -58,13 +70,12 @@ function GroupBySelectUi(props) {
|
||||||
singleSelection={{ asPlainText: true }}
|
singleSelection={{ asPlainText: true }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupBySelectUi.propTypes = {
|
GroupBySelectUi.propTypes = {
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
value: PropTypes.string
|
value: PropTypes.string,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GroupBySelect = injectI18n(GroupBySelectUi);
|
export const GroupBySelect = injectI18n(GroupBySelectUi);
|
||||||
export default GroupBySelect;
|
|
||||||
|
|
|
@ -20,18 +20,26 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { get, find } from 'lodash';
|
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 createTextHandler from '../lib/create_text_handler';
|
||||||
import createSelectHandler from '../lib/create_select_handler';
|
import createSelectHandler from '../lib/create_select_handler';
|
||||||
import FieldSelect from '../aggs/field_select';
|
import FieldSelect from '../aggs/field_select';
|
||||||
import MetricSelect from '../aggs/metric_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 { injectI18n, FormattedMessage } from '@kbn/i18n/react';
|
||||||
import { FIELD_TYPES } from '../../../common/field_types';
|
import { FIELD_TYPES } from '../../../common/field_types';
|
||||||
|
|
||||||
const DEFAULTS = { terms_direction: 'desc', terms_size: 10, terms_order_by: '_count' };
|
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 htmlId = htmlIdGenerator();
|
||||||
const handleTextChange = createTextHandler(onChange);
|
const handleTextChange = createTextHandler(onChange);
|
||||||
const handleSelectChange = createSelectHandler(onChange);
|
const handleSelectChange = createSelectHandler(onChange);
|
||||||
|
@ -76,6 +84,7 @@ export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesMode
|
||||||
<GroupBySelect
|
<GroupBySelect
|
||||||
value={model.split_mode}
|
value={model.split_mode}
|
||||||
onChange={handleSelectChange('split_mode')}
|
onChange={handleSelectChange('split_mode')}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
@ -93,6 +102,8 @@ export const SplitByTermsUI = ({ onChange, indexPattern, intl, model: seriesMode
|
||||||
onChange={handleSelectChange('terms_field')}
|
onChange={handleSelectChange('terms_field')}
|
||||||
value={model.terms_field}
|
value={model.terms_field}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
|
type={'terms'}
|
||||||
/>
|
/>
|
||||||
</EuiFormRow>
|
</EuiFormRow>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
|
@ -186,7 +197,8 @@ SplitByTermsUI.propTypes = {
|
||||||
model: PropTypes.object,
|
model: PropTypes.object,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
indexPattern: PropTypes.string,
|
indexPattern: PropTypes.string,
|
||||||
fields: PropTypes.object
|
fields: PropTypes.object,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SplitByTerms = injectI18n(SplitByTermsUI);
|
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: [''],
|
extractedIndexPatterns: [''],
|
||||||
};
|
};
|
||||||
this.onBrush = brushHandler(props.vis.API.timeFilter);
|
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());
|
this.visData$ = this.visDataSubject.asObservable().pipe(share());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ class VisEditor extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.isEditorMode) {
|
if (this.props.isEditorMode) {
|
||||||
const { params, fields } = this.props.vis;
|
const { params } = this.props.vis;
|
||||||
const extractedIndexPatterns = extractIndexPatterns(params, fields);
|
const extractedIndexPatterns = extractIndexPatterns(params);
|
||||||
|
|
||||||
if (!isEqual(this.state.extractedIndexPatterns, extractedIndexPatterns)) {
|
if (!isEqual(this.state.extractedIndexPatterns, extractedIndexPatterns)) {
|
||||||
fetchFields(extractedIndexPatterns)
|
fetchFields(extractedIndexPatterns)
|
||||||
|
|
|
@ -41,7 +41,8 @@ function GaugeSeriesUi(props) {
|
||||||
disableAdd,
|
disableAdd,
|
||||||
selectedTab,
|
selectedTab,
|
||||||
visible,
|
visible,
|
||||||
intl
|
intl,
|
||||||
|
uiRestrictions
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const defaults = { label: '' };
|
const defaults = { label: '' };
|
||||||
|
@ -78,6 +79,7 @@ function GaugeSeriesUi(props) {
|
||||||
fields={fields}
|
fields={fields}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
model={model}
|
model={model}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -227,6 +229,7 @@ GaugeSeriesUi.propTypes = {
|
||||||
switchTab: PropTypes.func,
|
switchTab: PropTypes.func,
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const GaugeSeries = injectI18n(GaugeSeriesUi);
|
const GaugeSeries = injectI18n(GaugeSeriesUi);
|
||||||
|
|
|
@ -39,7 +39,8 @@ function MarkdownSeriesUi(props) {
|
||||||
disableAdd,
|
disableAdd,
|
||||||
selectedTab,
|
selectedTab,
|
||||||
visible,
|
visible,
|
||||||
intl
|
intl,
|
||||||
|
uiRestrictions
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const defaults = { label: '', var_name: '' };
|
const defaults = { label: '', var_name: '' };
|
||||||
|
@ -76,6 +77,7 @@ function MarkdownSeriesUi(props) {
|
||||||
fields={fields}
|
fields={fields}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
model={model}
|
model={model}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -197,6 +199,7 @@ MarkdownSeriesUi.propTypes = {
|
||||||
switchTab: PropTypes.func,
|
switchTab: PropTypes.func,
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MarkdownSeries = injectI18n(MarkdownSeriesUi);
|
const MarkdownSeries = injectI18n(MarkdownSeriesUi);
|
||||||
|
|
|
@ -41,7 +41,8 @@ function MetricSeriesUi(props) {
|
||||||
disableAdd,
|
disableAdd,
|
||||||
selectedTab,
|
selectedTab,
|
||||||
visible,
|
visible,
|
||||||
intl
|
intl,
|
||||||
|
uiRestrictions
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const defaults = { label: '' };
|
const defaults = { label: '' };
|
||||||
|
@ -78,6 +79,7 @@ function MetricSeriesUi(props) {
|
||||||
fields={fields}
|
fields={fields}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
model={model}
|
model={model}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -232,6 +234,7 @@ MetricSeriesUi.propTypes = {
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
togglePanelActivation: PropTypes.func,
|
togglePanelActivation: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MetricSeries = injectI18n(MetricSeriesUi);
|
const MetricSeries = injectI18n(MetricSeriesUi);
|
||||||
|
|
|
@ -202,6 +202,7 @@ TableSeries.propTypes = {
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
togglePanelActivation: PropTypes.func,
|
togglePanelActivation: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectI18n(TableSeries);
|
export default injectI18n(TableSeries);
|
||||||
|
|
|
@ -41,7 +41,8 @@ const TimeseriesSeries = injectI18n(function (props) {
|
||||||
selectedTab,
|
selectedTab,
|
||||||
onChange,
|
onChange,
|
||||||
visible,
|
visible,
|
||||||
intl
|
intl,
|
||||||
|
uiRestrictions
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const defaults = { label: '' };
|
const defaults = { label: '' };
|
||||||
|
@ -78,6 +79,7 @@ const TimeseriesSeries = injectI18n(function (props) {
|
||||||
fields={fields}
|
fields={fields}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
model={model}
|
model={model}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -236,6 +238,7 @@ TimeseriesSeries.propTypes = {
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
togglePanelActivation: PropTypes.func,
|
togglePanelActivation: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default injectI18n(TimeseriesSeries);
|
export default injectI18n(TimeseriesSeries);
|
||||||
|
|
|
@ -42,7 +42,8 @@ const TopNSeries = injectI18n(function (props) {
|
||||||
disableAdd,
|
disableAdd,
|
||||||
selectedTab,
|
selectedTab,
|
||||||
visible,
|
visible,
|
||||||
intl
|
intl,
|
||||||
|
uiRestrictions,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const handleChange = createTextHandler(onChange);
|
const handleChange = createTextHandler(onChange);
|
||||||
|
@ -55,7 +56,7 @@ const TopNSeries = injectI18n(function (props) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
let seriesBody;
|
let seriesBody;
|
||||||
if (selectedTab === 'metrics') {
|
if (selectedTab === 'metrics') {
|
||||||
const handleSort = (data) => {
|
const handleSort = data => {
|
||||||
const metrics = data.map(id => model.metrics.find(m => m.id === id));
|
const metrics = data.map(id => model.metrics.find(m => m.id === id));
|
||||||
props.onChange({ metrics });
|
props.onChange({ metrics });
|
||||||
};
|
};
|
||||||
|
@ -76,6 +77,7 @@ const TopNSeries = injectI18n(function (props) {
|
||||||
fields={fields}
|
fields={fields}
|
||||||
panel={panel}
|
panel={panel}
|
||||||
model={model}
|
model={model}
|
||||||
|
uiRestrictions={uiRestrictions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -226,6 +228,7 @@ TopNSeries.propTypes = {
|
||||||
toggleVisible: PropTypes.func,
|
toggleVisible: PropTypes.func,
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
togglePanelActivation: PropTypes.func,
|
togglePanelActivation: PropTypes.func,
|
||||||
|
uiRestrictions: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default TopNSeries;
|
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';
|
import { uniq } from 'lodash';
|
||||||
|
|
||||||
export function extractIndexPatterns(params, fetchedFields) {
|
export function extractIndexPatterns(params, fetchedFields = {}) {
|
||||||
const patternsToFetch = [];
|
const patternsToFetch = [];
|
||||||
|
|
||||||
if (!fetchedFields[params.index_pattern]) {
|
if (!fetchedFields[params.index_pattern]) {
|
||||||
|
|
|
@ -33,10 +33,32 @@ export class DefaultSearchCapabilities {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get whiteListedMetrics() {
|
||||||
|
return this.createUiRestriction();
|
||||||
|
}
|
||||||
|
|
||||||
|
get whiteListedGroupByFields() {
|
||||||
|
return this.createUiRestriction();
|
||||||
|
}
|
||||||
|
|
||||||
|
get uiRestrictions() {
|
||||||
|
return {
|
||||||
|
whiteListedMetrics: this.whiteListedMetrics,
|
||||||
|
whiteListedGroupByFields: this.whiteListedGroupByFields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
get searchTimezone() {
|
get searchTimezone() {
|
||||||
return getTimezoneFromRequest(this.request);
|
return getTimezoneFromRequest(this.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUiRestriction(restrictionsObject) {
|
||||||
|
return {
|
||||||
|
'*': !restrictionsObject,
|
||||||
|
...(restrictionsObject || {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
parseInterval(interval) {
|
parseInterval(interval) {
|
||||||
return parseInterval(interval);
|
return parseInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,27 +36,37 @@ export async function getSeriesData(req, panel) {
|
||||||
const body = (await Promise.all(bodiesPromises))
|
const body = (await Promise.all(bodiesPromises))
|
||||||
.reduce((acc, items) => acc.concat(items), []);
|
.reduce((acc, items) => acc.concat(items), []);
|
||||||
|
|
||||||
return searchRequest.search({ body })
|
const meta = {
|
||||||
.then(data => {
|
type: panel.type,
|
||||||
const series = data.map(handleResponseBody(panel));
|
uiRestrictions: capabilities.uiRestrictions,
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import buildRequestBody from './table/build_request_body';
|
import buildRequestBody from './table/build_request_body';
|
||||||
import handleErrorResponse from './handle_error_response';
|
import handleErrorResponse from './handle_error_response';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
|
@ -33,14 +32,27 @@ export async function getTableData(req, panel) {
|
||||||
const { indexPatternObject } = await getIndexPatternObject(req, panelIndexPattern);
|
const { indexPatternObject } = await getIndexPatternObject(req, panelIndexPattern);
|
||||||
const body = buildRequestBody(req, panel, esQueryConfig, indexPatternObject, capabilities);
|
const body = buildRequestBody(req, panel, esQueryConfig, indexPatternObject, capabilities);
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
type: panel.type,
|
||||||
|
uiRestrictions: capabilities.uiRestrictions,
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [resp] = await searchRequest.search({ body });
|
const [resp] = await searchRequest.search({ body });
|
||||||
const buckets = get(resp, 'aggregations.pivot.buckets', []);
|
const buckets = get(resp, 'aggregations.pivot.buckets', []);
|
||||||
return { type: 'table', series: buckets.map(processBucket(panel)) };
|
|
||||||
|
return {
|
||||||
|
...meta,
|
||||||
|
series: buckets.map(processBucket(panel)),
|
||||||
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.body) {
|
if (err.body) {
|
||||||
err.response = 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)
|
return Promise.all(requests)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
return results.map(result => {
|
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) => {
|
return metricIds.map((id: string) => {
|
||||||
const infraMetricId: InfraMetric = (InfraMetric as any)[id];
|
const infraMetricId: InfraMetric = (InfraMetric as any)[id];
|
||||||
if (!infraMetricId) {
|
if (!infraMetricId) {
|
||||||
|
|
|
@ -3,18 +3,10 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with 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';
|
import { unitsMap } from '@elastic/datemath';
|
||||||
|
|
||||||
const leastCommonInterval = (num = 0, base = 0) => Math.max(Math.ceil(num / base) * base, base);
|
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);
|
const isCalendarInterval = ({ unit, value }) => value === 1 && ['calendar', 'mixed'].includes(unitsMap[unit].type);
|
||||||
|
|
||||||
export const getRollupSearchCapabilities = (DefaultSearchCapabilities) =>
|
export const getRollupSearchCapabilities = (DefaultSearchCapabilities) =>
|
||||||
|
@ -23,7 +15,13 @@ export const getRollupSearchCapabilities = (DefaultSearchCapabilities) =>
|
||||||
super(req, batchRequestsSupport, fieldsCapabilities);
|
super(req, batchRequestsSupport, fieldsCapabilities);
|
||||||
|
|
||||||
this.rollupIndex = rollupIndex;
|
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() {
|
get defaultTimeInterval() {
|
||||||
|
@ -34,6 +32,30 @@ export const getRollupSearchCapabilities = (DefaultSearchCapabilities) =>
|
||||||
return get(this.dateHistogram, 'time_zone', null);
|
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) {
|
getValidTimeInterval(userIntervalString) {
|
||||||
const parsedRollupJobInterval = this.parseInterval(this.defaultTimeInterval);
|
const parsedRollupJobInterval = this.parseInterval(this.defaultTimeInterval);
|
||||||
const parsedUserInterval = this.parseInterval(userIntervalString);
|
const parsedUserInterval = this.parseInterval(userIntervalString);
|
||||||
|
|
Loading…
Reference in a new issue