Skip to content

Commit

Permalink
Support kibana v7.0.0 (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Charlot authored Apr 24, 2019
1 parent a354db0 commit ada7cdb
Show file tree
Hide file tree
Showing 16 changed files with 321 additions and 368 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ We are Datasweet, a french startup providing full service (big) data solutions.

# Installation
This plugin is supported by :
- Kibana 6.7.x
- Kibana 6.6.x
- Kibana 6.5.x
- Kibana 7.0.x => v2.2.0
- Kibana 6.5.x to 6.7.x => v2.1.0
- Kibana 6.4.x => v2.0.0
- Kibana 5.6.x to 6.3.x => v1.1.3


Copy the last installation url for your version of Kibana from the [repository releases](https://github.com/datasweet/kibana-datasweet-formula/releases/latest).
```
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "datasweet_formula",
"version": "2.1.0",
"version": "2.2.0",
"description": "This Kibana plugin allows calculated metrics on any standard kibana visualizations.",
"main": "index.js",
"kibana": {
Expand Down Expand Up @@ -52,7 +52,6 @@
},
"dependencies": {
"expr-eval": "^1.2.1",
"lodash": "^3.10.1",
"percentile": "^1.2.1"
}
}
96 changes: 45 additions & 51 deletions public/agg_types/formula.js
Original file line number Diff line number Diff line change
@@ -1,62 +1,56 @@
import { capitalize, map } from 'lodash';
import * as magprov from 'ui/agg_types/metrics/metric_agg_type';
import * as ffprov from 'ui/registry/field_formats';
import { MetricAggType } from 'ui/agg_types/metrics/metric_agg_type';
import { fieldFormats } from 'ui/registry/field_formats';
import formulaEditor from './formula.html';
import formatterEditor from './formatter.html';

export function AggTypesMetricsFormulaProvider(Private) {
const MetricAggType = magprov.MetricAggType || Private(magprov.AggTypesMetricsMetricAggTypeProvider);
const fieldFormats = ffprov.fieldFormats || Private(ffprov.RegistryFieldFormatsProvider);
const defaultValue = null;
const formatters = map(['number', 'percent', 'boolean', 'bytes', 'numeral'], f => {
return { id: f, title: capitalize(f) };
});
const formatters = map(['number', 'percent', 'boolean', 'bytes', 'numeral'], f => {
return { id: f, title: capitalize(f) };
});

return new MetricAggType({
name: 'datasweet_formula',
title: 'Datasweet Formula',
subtype: 'Calculated Metrics',
hasNoDsl: true,
makeLabel: function (aggConfig) {
return 'Formula ' + aggConfig.id;
export const formulaMetricAgg = new MetricAggType({
name: 'datasweet_formula',
title: 'Datasweet Formula',
subtype: 'Calculated Metrics',
hasNoDsl: true,
makeLabel: function (aggConfig) {
return 'Formula ' + aggConfig.id;
},
params: [
{
name: 'formula',
editor: formulaEditor,
},
params: [
{
name: 'formula',
editor: formulaEditor,
},
{
name: 'formatter',
editor: formatterEditor,
getFormatters: function () {
return formatters;
}
},
{
name: 'numeralFormat'
{
name: 'formatter',
editor: formatterEditor,
getFormatters: function () {
return formatters;
}
],
getFormat: function (agg) {
const formatterId = agg.params.formatter;
if (!formatterId) {
return fieldFormats.getDefaultInstance('number');
}

if (formatterId === 'numeral') {
const format = agg.params.numeralFormat;
if (!format) {
return fieldFormats.getDefaultInstance('number');
}
},
{
name: 'numeralFormat'
}
],
getFormat: function (agg) {
const formatterId = agg.params.formatter;
if (!formatterId) {
return fieldFormats.getDefaultInstance('number');
}

const FieldFormat = fieldFormats.getType('number');
const f = new FieldFormat({ pattern: format });
return f;
if (formatterId === 'numeral') {
const format = agg.params.numeralFormat;
if (!format) {
return fieldFormats.getDefaultInstance('number');
}

return fieldFormats.getInstance(formatterId);
},
getValue: function () {
return defaultValue;
const FieldFormat = fieldFormats.getType('number');
const f = new FieldFormat({ pattern: format });
return f;
}
});
}
return fieldFormats.getInstance(formatterId);
},
getValue: function () {
return null;
}
});
6 changes: 2 additions & 4 deletions public/decorators/agg_config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { isBoolean } from 'lodash';
import * as prov from 'ui/vis/agg_config';
import { AggConfig } from 'ui/vis/agg_config';


export function decorateVisAggConfigProvider(Private) {
const AggConfig = prov.AggConfig || Private(prov.VisAggConfigProvider);

export function decorateVisAggConfig() {
Object.defineProperty(AggConfig.prototype, 'hidden', {
get: function () {
if (isBoolean(this.__hidden)) return this.__hidden; // current value
Expand Down
6 changes: 2 additions & 4 deletions public/decorators/agg_configs.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as prov from 'ui/vis/agg_configs';

export function decorateVisAggConfigsProvider(Private) {
const AggConfigs = prov.AggConfigs || Private(prov.VisAggConfigsProvider);
import { AggConfigs } from 'ui/vis/agg_configs';

export function decorateVisAggConfigs() {
/**
* Recursively removes undefined values from object.
* @param {*} obj
Expand Down
18 changes: 14 additions & 4 deletions public/decorators/agg_table.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import chrome from 'ui/chrome';
import { uiModules } from 'ui/modules';
import { TableTotalFormulaProvider } from './lib/apply_formula_total';
import { applyFormulaTotal } from './lib/apply_formula_total';
const appId = chrome.getApp().id;

// Defines the $scope.totalFunc allowed to perform the calculations
Expand All @@ -12,10 +12,9 @@ if (appId === 'kibana') {
.get('kibana')
.config(($provide) => {
// Decorates kbnAggTable default directive
$provide.decorator('kbnAggTableDirective', ($delegate, $controller, Private) => {
$provide.decorator('kbnAggTableDirective', ($delegate, $controller) => {
const directive = $delegate[0];
const controllerName = directive.controller;
const applyFormulaTotal = Private(TableTotalFormulaProvider);

directive.controller = function ($scope) {
angular.extend(this, $controller(controllerName, { $scope: $scope }));
Expand All @@ -27,7 +26,18 @@ if (appId === 'kibana') {
// Validations
if (!table) return;
const hasColumns = !!table.columns.length;
const hasFormulas = !!table.columns.find(c => c.aggConfig.type.name === 'datasweet_formula');

// Formatters
let hasFormulas = false;
table.columns.forEach((col, i) => {
if (col.aggConfig.type.name !== 'datasweet_formula') {
return;
}
hasFormulas = true;
$scope.formattedColumns[i].formatter = { convert: col.aggConfig.fieldFormatter('text') };
});

// Total
const isTotalFunctionAllowed = $scope.showTotal && TOTAL_FUNCTIONS_ALLOWED.includes($scope.totalFunc);
const shouldApplyFormula = hasColumns && hasFormulas && isTotalFunctionAllowed;
if (!shouldApplyFormula) return;
Expand Down
12 changes: 5 additions & 7 deletions public/decorators/agg_types.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import * as prov from 'ui/agg_types';
import { AggTypesMetricsFormulaProvider } from 'plugins/datasweet_formula/agg_types/formula';
import { aggTypes } from 'ui/agg_types';
import { formulaMetricAgg } from 'plugins/datasweet_formula/agg_types/formula';

export function decorateAggTypes(Private) {
const AggTypes = prov.aggTypes || Private(prov.AggTypesIndexProvider);
const AggFormula = Private(AggTypesMetricsFormulaProvider);
AggFormula.type = 'metrics';
AggTypes.push(AggFormula);
export function decorateAggTypes() {
formulaMetricAgg.type = 'metrics';
aggTypes.push(formulaMetricAgg);
};
32 changes: 10 additions & 22 deletions public/decorators/lib/__tests__/formula_parser.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,29 @@
import expect from 'expect.js';
import ngMock from 'ng_mock';
import 'ui/private';
import { FormulaParserProvider } from '../formula_parser';
import { formulaParser } from '../formula_parser';

describe('Formula Parser', () => {
let FormulaParser;

beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
FormulaParser = Private(FormulaParserProvider);
}));


it('unary operators must be wrapped', () => {
const parser = new FormulaParser();
expect(parser.evaluate('sqrt(9)')).to.equal(3);
expect(parser.evaluate('sqrt(agg1)', { 'agg1': [4,9,16]})).to.eql([2,3,4]);
expect(formulaParser.evaluate('sqrt(9)')).to.equal(3);
expect(formulaParser.evaluate('sqrt(agg1)', { 'agg1': [4,9,16]})).to.eql([2,3,4]);
});

it('binary operators must be wrapped', () => {
const parser = new FormulaParser();
const agg1 = [3,4,6];
const agg2 = [10,2,7];
const agg3 = [8,2];

expect(parser.evaluate('10 - 2')).to.equal(8);
expect(parser.evaluate('agg1 - 2', { agg1 })).to.eql([1,2,4]);
expect(parser.evaluate('10 - agg1', { agg1 })).to.eql([7,6,4]);
expect(parser.evaluate('agg1 - agg2', { agg1, agg2 })).to.eql([-7,2,-1]);
expect(parser.evaluate('agg1 - agg3', { agg1, agg3 })).to.eql([-5,2,6]);
expect(parser.evaluate('agg3 - agg1', { agg1, agg3 })).to.eql([5,-2,-6]);
expect(formulaParser.evaluate('10 - 2')).to.equal(8);
expect(formulaParser.evaluate('agg1 - 2', { agg1 })).to.eql([1,2,4]);
expect(formulaParser.evaluate('10 - agg1', { agg1 })).to.eql([7,6,4]);
expect(formulaParser.evaluate('agg1 - agg2', { agg1, agg2 })).to.eql([-7,2,-1]);
expect(formulaParser.evaluate('agg1 - agg3', { agg1, agg3 })).to.eql([-5,2,6]);
expect(formulaParser.evaluate('agg3 - agg1', { agg1, agg3 })).to.eql([5,-2,-6]);
});

it('ternary ops condition must be deactivated', () => {
const parser = new FormulaParser();
expect(function () {
parser.evaluate('true ? 1 : 0');
formulaParser.evaluate('true ? 1 : 0');
}).to.throwException(/\?/);
});
});
Loading

0 comments on commit ada7cdb

Please sign in to comment.