Skip to content

Commit

Permalink
[Vis: Default editor] EUIficate Sub agg control (elastic#37979)
Browse files Browse the repository at this point in the history
* EUIficate metric agg control

* Fix translation errors

* Display agg error underneath the last bucket agg form control

* Update functional test

* Update error message

* Update parent_pipeline_agg_controller.js

* Fix validation when metricAgg is invalid

* Show error message when a filed is selected

* Delete _terms_helper.tsx

* Remove extra empty line

* Update parent_pipeline_agg_helper.js

* Update selector for test
  • Loading branch information
maryia-lapata committed Jun 13, 2019
1 parent 704d9b7 commit 11b1fe5
Show file tree
Hide file tree
Showing 16 changed files with 198 additions and 197 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,23 @@
* under the License.
*/

import { AggConfig } from 'ui/vis';
import { i18n } from '@kbn/i18n';

const aggFilter = [
'!top_hits',
'!percentiles',
'!median',
'!std_dev',
'!derivative',
'!moving_avg',
'!serial_diff',
'!cumulative_sum',
'!avg_bucket',
'!max_bucket',
'!min_bucket',
'!sum_bucket',
];

// Returns true if the agg is compatible with the terms bucket
function isCompatibleAgg(agg: AggConfig) {
return !aggFilter.includes(`!${agg.type.name}`);
}
import { AggConfig } from '../vis/agg_config';

function safeMakeLabel(agg: AggConfig) {
try {
return agg.makeLabel();
} catch (e) {
return i18n.translate('common.ui.aggTypes.buckets.terms.aggNotValidLabel', {
return i18n.translate('common.ui.aggTypes.aggNotValidLabel', {
defaultMessage: '- agg not valid -',
});
}
}

export { aggFilter, isCompatibleAgg, safeMakeLabel };
function isCompatibleAggregation(aggFilter: string[]) {
return (agg: AggConfig) => {
return !aggFilter.includes(`!${agg.type.name}`);
};
}

export { safeMakeLabel, isCompatibleAggregation };
3 changes: 1 addition & 2 deletions src/legacy/ui/public/agg_types/buckets/terms.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ import { createFilterTerms } from './create_filter/terms';
import { wrapWithInlineComp } from './_inline_comp_wrapper';
import { buildOtherBucketAgg, mergeOtherBucketAggResponse, updateMissingBucket } from './_terms_other_bucket_helper';
import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format';
import { aggFilter } from './_terms_helper';
import orderAggTemplate from '../controls/order_agg.html';
import { OrderParamEditor } from '../controls/order';
import { OrderAggParamEditor } from '../controls/order_agg';
import { OrderAggParamEditor, aggFilter } from '../controls/order_agg';
import { SizeParamEditor } from '../controls/size';
import { MissingBucketParamEditor } from '../controls/missing_bucket';
import { OtherBucketParamEditor } from '../controls/other_bucket';
Expand Down
109 changes: 109 additions & 0 deletions src/legacy/ui/public/agg_types/controls/metric_agg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* 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 React, { useEffect } from 'react';
import { EuiFormRow, EuiSelect } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AggParamEditorProps } from 'ui/vis/editors/default';
import { safeMakeLabel, isCompatibleAggregation } from '../agg_utils';

const aggFilter = ['!top_hits', '!percentiles', '!percentile_ranks', '!median', '!std_dev'];
const isCompatibleAgg = isCompatibleAggregation(aggFilter);
const EMPTY_VALUE = 'EMPTY_VALUE';

function MetricAggParamEditor({
agg,
value,
showValidation,
setValue,
setValidity,
setTouched,
responseValueAggs,
}: AggParamEditorProps<string>) {
const label = i18n.translate('common.ui.aggTypes.metricLabel', {
defaultMessage: 'Metric',
});
const isValid = !!value;

useEffect(
() => {
setValidity(isValid);
},
[isValid]
);

useEffect(
() => {
if (responseValueAggs && value && value !== 'custom') {
// ensure that metricAgg is set to a valid agg
const respAgg = responseValueAggs
.filter(isCompatibleAgg)
.find(aggregation => aggregation.id === value);

if (!respAgg) {
setValue();
}
}
},
[responseValueAggs]
);

const options = responseValueAggs
? responseValueAggs
.filter(respAgg => respAgg.type.name !== agg.type.name)
.map(respAgg => ({
text: i18n.translate('common.ui.aggTypes.definiteMetricLabel', {
defaultMessage: 'Metric: {safeMakeLabel}',
values: {
safeMakeLabel: safeMakeLabel(respAgg),
},
}),
value: respAgg.id,
disabled: !isCompatibleAgg(respAgg),
}))
: [];

options.push({
text: i18n.translate('common.ui.aggTypes.customMetricLabel', {
defaultMessage: 'Custom metric',
}),
value: 'custom',
disabled: false,
});

if (!value) {
options.unshift({ text: '', value: EMPTY_VALUE, disabled: false });
}

return (
<EuiFormRow label={label} fullWidth={true} isInvalid={showValidation ? !isValid : false}>
<EuiSelect
options={options}
value={value || EMPTY_VALUE}
onChange={ev => setValue(ev.target.value)}
fullWidth={true}
isInvalid={showValidation ? !isValid : false}
onBlur={setTouched}
data-test-subj={`visEditorSubAggMetric${agg.id}`}
/>
</EuiFormRow>
);
}

export { MetricAggParamEditor };
20 changes: 18 additions & 2 deletions src/legacy/ui/public/agg_types/controls/order_agg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,23 @@ import React, { useEffect } from 'react';
import { EuiFormRow, EuiSelect } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AggParamEditorProps } from 'ui/vis/editors/default';
import { safeMakeLabel, isCompatibleAgg } from '../buckets/_terms_helper';
import { safeMakeLabel, isCompatibleAggregation } from '../agg_utils';

const aggFilter = [
'!top_hits',
'!percentiles',
'!median',
'!std_dev',
'!derivative',
'!moving_avg',
'!serial_diff',
'!cumulative_sum',
'!avg_bucket',
'!max_bucket',
'!min_bucket',
'!sum_bucket',
];
const isCompatibleAgg = isCompatibleAggregation(aggFilter);

function OrderAggParamEditor({
agg,
Expand Down Expand Up @@ -116,4 +132,4 @@ function OrderAggParamEditor({
);
}

export { OrderAggParamEditor };
export { OrderAggParamEditor, aggFilter };
49 changes: 7 additions & 42 deletions src/legacy/ui/public/agg_types/controls/sub_agg.html
Original file line number Diff line number Diff line change
@@ -1,43 +1,8 @@
<div ng-controller="aggParam.controller">
<div class="form-group">
<label
for="visEditorSubAggMetric{{agg.id}}"
i18n-id="common.ui.aggTypes.metricLabel"
i18n-default-message="Metric"
></label>
<select
id="visEditorSubAggMetric{{agg.id}}"
name="metricAgg"
ng-model="agg.params.metricAgg"
agg="agg"
required
validate-agg
class="form-control">
<option
ng-repeat="respAgg in responseValueAggs track by respAgg.id"
value="{{respAgg.id}}"
ng-if="respAgg.type.name !== agg.type.name"
ng-disabled="isDisabledAgg(respAgg)"
ng-selected="agg.params.metricAgg === respAgg.id"
i18n-id="common.ui.aggTypes.definiteMetricLabel"
i18n-default-message="metric: {safeMakeLabel}"
i18n-values="{ safeMakeLabel: safeMakeLabel(respAgg) }"
></option>
<option
value="custom"
ng-selected="agg.params.metricAgg === 'custom'"
i18n-id="common.ui.aggTypes.customMetricLabel"
i18n-default-message="Custom Metric"
></option>
</select>
</div>
<div ng-if="agg.params.metricAgg === 'custom'" class="visEditorAgg__subAgg">
<ng-form name="customMetricForm">
<vis-editor-agg-params
index-pattern="agg.getIndexPattern()"
agg="agg.params.customMetric"
group-name="'metrics'">
</vis-editor-agg-params>
</ng-form>
</div>
<div ng-controller="aggParam.controller" ng-show="agg.params.metricAgg === 'custom'" class="visEditorAgg__subAgg">
<vis-editor-agg-params
index-pattern="agg.getIndexPattern()"
agg="agg.params.customMetric"
ng-if="agg.params.metricAgg === 'custom'"
group-name="'metrics'">
</vis-editor-agg-params>
</div>
2 changes: 1 addition & 1 deletion src/legacy/ui/public/agg_types/controls/top_field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function TopFieldParamEditor(props: AggParamEditorProps<FieldParamType>) {
const compatibleAggs = getCompatibleAggs(props.agg, props.visName);
let customError;

if (!compatibleAggs.length) {
if (props.value && !compatibleAggs.length) {
customError = i18n.translate('common.ui.aggTypes.aggregateWith.noAggsErrorTooltip', {
defaultMessage: 'The chosen field has no compatible aggregations.',
});
Expand Down
58 changes: 0 additions & 58 deletions src/legacy/ui/public/agg_types/directives/validate_agg.js

This file was deleted.

1 change: 0 additions & 1 deletion src/legacy/ui/public/agg_types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
* under the License.
*/

import './directives/validate_agg';
import './agg_params';
import { IndexedArray } from '../indexed_array';
import { countMetricAgg } from './metrics/count';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,36 @@
*/

import _ from 'lodash';
import { safeMakeLabel } from './safe_make_label';
import { i18n } from '@kbn/i18n';

const parentPipelineAggController = function ($scope) {

$scope.safeMakeLabel = safeMakeLabel;

$scope.$watch('responseValueAggs', updateOrderAgg);
$scope.$watch('agg.params.metricAgg', updateOrderAgg);

$scope.$on('$destroy', function () {
const lastBucket = _.findLast($scope.state.aggs, agg => agg.type.type === 'buckets');
if ($scope.aggForm && $scope.aggForm.agg) {
$scope.aggForm.agg.$setValidity('bucket', true);
}
const lastBucket = _.findLast($scope.state.aggs, agg => agg.type && agg.type.type === 'buckets');

if (lastBucket && lastBucket.error) {
delete lastBucket.error;
}
});

$scope.isDisabledAgg = function (agg) {
const invalidAggs = ['top_hits', 'percentiles', 'percentile_ranks', 'median', 'std_dev'];
return Boolean(invalidAggs.find(invalidAgg => invalidAgg === agg.type.name));
};

function checkBuckets() {
const lastBucket = _.findLast($scope.state.aggs, agg => agg.type.type === 'buckets');
const lastBucket = _.findLast($scope.state.aggs, agg => agg.type && agg.type.type === 'buckets');
const bucketHasType = lastBucket && lastBucket.type;
const bucketIsHistogram = bucketHasType && ['date_histogram', 'histogram'].includes(lastBucket.type.name);
const canUseAggregation = lastBucket && bucketIsHistogram;

// remove errors on all buckets
_.each($scope.state.aggs, agg => { if (agg.error) delete agg.error; });

if ($scope.aggForm.agg) {
$scope.aggForm.agg.$setValidity('bucket', canUseAggregation);
}
if (canUseAggregation) {
lastBucket.params.min_doc_count = (lastBucket.type.name === 'histogram') ? 1 : 0;
} else {
if (lastBucket) {
const type = $scope.agg.type.title;
lastBucket.error = i18n.translate('common.ui.aggTypes.metrics.wrongLastBucketTypeErrorMessage', {
defaultMessage: 'Last bucket aggregation must be "Date Histogram" or "Histogram" when using "{type}" metric aggregation!',
defaultMessage: 'Last bucket aggregation must be "Date Histogram" or "Histogram" when using "{type}" metric aggregation.',
values: { type },
description: 'Date Histogram and Histogram should not be translated'
});
Expand All @@ -79,9 +65,6 @@ const parentPipelineAggController = function ($scope) {

// we aren't creating a custom aggConfig
if (metricAgg !== 'custom') {
if (!$scope.state.aggs.find(agg => agg.id === metricAgg)) {
params.metricAgg = null;
}
params.customMetric = null;
return;
}
Expand Down
Loading

0 comments on commit 11b1fe5

Please sign in to comment.