Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce EditorConfigProvider to customize vis editor #20519

Merged
merged 13 commits into from
Aug 10, 2018

Conversation

timroes
Copy link
Contributor

@timroes timroes commented Jul 6, 2018

This PR introduces the basic EditorConfigProvider which will be needed for roll-up support to limit down parameters to specific values. It introduces the base, fixedValue and hidden configurations.

It does not yet implement anything around date histograms and date interval multiples. Those will be in a separate PR to keep the size of the PRs reviewable.

Sample code on how to use this:

editorConfigProviders.register((aggType, indexPattern, aggConfig) => {
  if (aggConfig.params.field && aggConfig.params.field.name === 'bytes') {
    return {
      'intervalBase': {
        fixedValue: 50
      },
      'interval': {
        base: 50,
        warning: 'You can only specify multiples of 50, because of roll-up...',
      }
    };
  }
  if (aggConfig.type && aggConfig.type.name === 'date_histogram') {
    return {
      'time_zone': {
        fixedValue: 'UTC'
      }
    };
  }
  return {};
});

@jen-huang Could you please check again if this PR satisfies the need around base, fixed values and hiding. Also since we don't leverage any of those yet in the UI, we cannot provide functional tests with those. Please make sure that roll-up feature has a high coverage on functional tests, that the vis editor really behaves properly (hides, only allow multiples, etc.) according to the configuration.

@timroes timroes added WIP Work in progress Feature:Visualizations Generic visualization features (in case no more specific feature label is available) v7.0.0 labels Jul 6, 2018
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@cjcenizal cjcenizal mentioned this pull request Jul 12, 2018
52 tasks
@jen-huang jen-huang mentioned this pull request Jul 23, 2018
10 tasks
@timroes timroes added v6.5.0 and removed WIP Work in progress labels Aug 6, 2018
@timroes timroes changed the title [WIP] Editor config Introduce EditorConfigProvider to customize vis editor Aug 6, 2018
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@cjcenizal cjcenizal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great @timroes! Thanks for all of the comments and clear tests. I had some suggestions but nothing major.

expect(config.base).toBe(40);
});

it('should throw on combinding fixedValue with base', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "combinding" -> "combining"

// If not we'll throw an error.
throw new Error(`Two EditorConfigProviders provided different fixed values for field ${paramName}:
${merged.fixedValue} !== ${current.fixedValue}`);
} else if (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be just a regular if instead of else if, since the first condition is an early exit. This will make the code a bit easier to scan.

// that are the multiple of the specific base value, but since there is no use-case for that
// right now, this isn't implemented.
throw new Error(`Tried to provide a fixedValue and a base for param ${paramName}.`);
} else if (this.isBaseParam(current) && this.isBaseParam(merged)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

return {
base: leastCommonMultiple(current.base, merged.base),
};
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be inside a condition at all, since the previous conditions all throw or return.

return {
fixedValue: current.fixedValue,
};
} else if (this.isBaseParam(current)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be an if.

@@ -28,6 +28,29 @@ import { uiModules } from '../../../modules';
import { documentationLinks } from '../../../documentation_links/documentation_links';
import aggParamsTemplate from './agg_params.html';
import { aggTypeFilters } from '../../../agg_types/filter';
import { editorConfigProviders } from '../config/editor_config_providers';

// TODO: This is just an example how to use this, and will be removed later.
Copy link
Contributor

@cjcenizal cjcenizal Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still need to be here? It does seem useful, so maybe we should remove the TODO annotation and just change this comment to be This is an example of how to use this.?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait why is this still there? Oh yeah I forgot to push my last commit from yesterday :D

if (config.hasOwnProperty('fixedValue')) {
$scope.agg.params[param] = config.fixedValue;
}
// TODO: process more editor config?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should have been removed

const isExtraParam = i > 0;
if (!hasIndexedFields && isExtraParam) { // don't draw the rest of the options if there are no indexed fields.
$scope.agg.type.params
.filter(param => !get($scope, ['editorConfig', param.name, 'hidden'], false))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make this easier to scan, can we assign this to a new variable or add a comment explaining what the intention is?

const visibleParams =
  $scope.agg.type.params.filter(param => !get($scope, ['editorConfig', param.name, 'hidden'], false));

aggConfig: AggConfig
) => EditorConfig;

function greatestCommonDivisor(a: number, b: number): number {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jen-huang Weren't you looking for utilities like this?

These seem general enough to belong in their own math module in ui/public, with a couple unit tests. What do you think @timroes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap, I will move them

return configs.reduce((output, conf) => {
Object.entries(conf).forEach(([paramName, paramConfig]) => {
if (!output[paramName]) {
output[paramName] = paramConfig;
Copy link
Contributor

@cjcenizal cjcenizal Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we're not mutating paramConfig anywhere in this code but is it possible someone might add code that adds properties to it in the future (outside of this module)? If so, is it worth being defensive and just creating a shallow copy here? After all, we do that on line 122 so we may as well retain parity.

output[paramName] = { ... paramConfig };

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@jen-huang jen-huang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I pulled this down and tested by creating a configuration that hides otherBucket and multipleBucket params, as well as configuring interval for histograms and timezone for date histogram based on rollup capabilities.

Just have a small question and suggestion regarding intervals.

    return {
      'intervalBase': {
        fixedValue: 50
      },
      'interval': {
        base: 50
      }
    };

Why do we need both intervalBase and interval? Would there be a case where intervalBase.fixedValue and interval.base are different?

@@ -14,7 +14,8 @@
type="number"
class="form-control"
name="interval"
min="0"
min="{{editorConfig.interval.base || 0}}"
step="{{editorConfig.interval.base}}"

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I have editor hints still on the list, but wanted to pull it into a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually still added it to this PR.

@timroes
Copy link
Contributor Author

timroes commented Aug 8, 2018

@jen-huang The reason why there are two different parameters is in the way aggregations in visualizations work. Basically everything needed to calculate the DSL must be an argument to the aggregation configuration, like interval. Since the auto scaling is taking part when calculating the DSL, we'll need to have the base that we want to use available at that point in time, meaning we need to have it as an argument to the aggConfig. So that explains why we need an intervalBase parameter to the pipeline (in addition to the interval which is the targeted interval, but might be scaled).

The other part of that question is: why do you need to write it out separately in the editor config? We could indeed say, whenever we hit a base setting for a parameter (which by itself just configured the UI to have stepped sizes in there), we will also set this value to the <paramName>Base parameter on aggConfig. That way the editor config provider itself would only need to return one thing and we let the editor handle splitting this up into the options it needs (1. fix the UI to it, and 2. set the appropriate base parameter on the aggconfig). My concern why I didn't want to have it that was was more and more implicit behavior in the editor, that noone will understand in a couple of months/years anymore.

We already have a lot of implicit behavior in there, and that way we would need to add another one which potentially is also error prone, like how is the parameter to the aggConfig called, that needs that base value? We always assume <paramName>Base? What if you set base, but the editor won't find a <paramName>Base interval on that aggConfig?

For that reason I rather tend not to do stuff implicitly anymore, but rather make them explicit. You want to set a value for the base that the aggregation uses to calculate - set it explicitly. You want the editor to only allow to configure a specific parameter to a specific base - set it explicitly.

Technically setting those separate would work technically: you could "step" the UI without forcing the scaling to come up with a multiple of that. Or always force the interval it ends up with being a specific multiple, and still let the user configure whatever they want in the UI. Is there a real use-case for that? I don't think so.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@jen-huang jen-huang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that explanation and adding the warning. LGTM!

Copy link
Member

@ppisljar ppisljar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left some small comments i hope can still get it, apart from that LGTM, gave just a quick look to the code.

self.aggType = aggTypes.byName[self.aggType];
}
constructor(opts) {
const self = this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get rid of self

const self = this;
paramValues = _.clone(paramValues);
write(paramValues, modifyAggConfig = null) {
const self = this;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

import { leastCommonMultiple } from '../../../utils/math';
import { EditorConfig, EditorParamConfig, FixedParam, NumericIntervalParam } from './types';

type EditorConfigProvider = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we use a different name for this ... Provider is currently used to indicate you will need to call it with Private to get the actual thing out of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I struggled with that too. The problem is I couldn't come up with a better name (Service would sound more like the central class, and couldn't find much better names). We also just had a discussion with CJ and Jen, and couldn't find any better name. So I'll use CJ's argument here, that we are trying to remove Private in the long run and it shouldn't keep the monopoly on that word, since it makes sense in other places too where it's more conventional.

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@timroes timroes merged commit 133d25a into elastic:master Aug 10, 2018
@timroes timroes deleted the editor-config branch August 10, 2018 18:10
timroes added a commit to timroes/kibana that referenced this pull request Aug 10, 2018
* Add possibility to specify base for histogram interval

* Move AggParamsWriter to ES6

* Add param for time_zone in date_histogram

* Prevent writing intervalBase to DSL

* Add basic EditorConfig providers

* Merge multiple configs together

* Allow hiding parameters

* Improve config merging and add tests

* Remove TODO

* Implement review feedback

* Add warning to parameter

* Remove unneeded self
timroes added a commit that referenced this pull request Aug 11, 2018
* Add possibility to specify base for histogram interval

* Move AggParamsWriter to ES6

* Add param for time_zone in date_histogram

* Prevent writing intervalBase to DSL

* Add basic EditorConfig providers

* Merge multiple configs together

* Allow hiding parameters

* Improve config merging and add tests

* Remove TODO

* Implement review feedback

* Add warning to parameter

* Remove unneeded self
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Visualizations Generic visualization features (in case no more specific feature label is available) v6.5.0 v7.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants