Skip to content

Commit

Permalink
Kibana v6.5.x to v6.7.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Thomas Charlot committed Apr 19, 2019
1 parent 8efb2a8 commit a354db0
Show file tree
Hide file tree
Showing 12 changed files with 1,424 additions and 842 deletions.
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ We are Datasweet, a french startup providing full service (big) data solutions.
![tutorial-datasweet-formula](docs/img/tutorial-datasweet-formula.gif)

# Installation
This plugin is supported by :
- Kibana 6.4.x
- Kibana 5.6
This plugin is supported by :
- Kibana 6.7.x
- Kibana 6.6.x
- Kibana 6.5.x

Copy the last installation url for your version of Kibana from the [repository releases](https://github.com/datasweet/kibana-datasweet-formula/releases/latest).
```
Expand All @@ -28,11 +29,22 @@ Check out what it can do in the [documentation.](http://www.datasweet.fr/dataswe

See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. Once you have completed that, use the following yarn scripts.

- `yarn kbn bootstrap`

Install dependencies and crosslink Kibana and all projects/plugins.

> ***IMPORTANT:*** Use this script instead of `yarn` to install dependencies when switching branches, and re-run it whenever your dependencies change.
> ***IMPORTANT:*** You must have the following tree :
- kibana/
- kibana-extra/
- kibana-datasweet-formula/

```bash
cd kibana
git checkout vX
rm -Rf node_modules/
nvm use
yarn kbn bootstrap
cd ../kibana-extra/kibana-datasweet-formula/
rm -Rf node_modules/
yarn kbn bootstrap
yarn start
```

- `yarn start`

Expand Down
39 changes: 10 additions & 29 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,13 @@ if [ -n "$1" ]; then
exit
fi

build 6.4.2
build 6.4.1
build 6.4.0

build 6.3.2
build 6.3.1
build 6.3.0
build 6.2.4
build 6.2.3
build 6.2.2
build 6.2.1
build 6.2.0

build 6.1.3
build 6.1.2
build 6.1.1
build 6.1.0

build 6.0.1
build 6.0.0

build 5.6.7
build 5.6.6
build 5.6.5
build 5.6.4
build 5.6.3
build 5.6.2
build 5.6.1
build 5.6.0
build 6.7.1
build 6.7.0
build 6.6.2
build 6.6.1
build 6.6.0
build 6.5.4
build 6.5.3
build 6.5.2
build 6.5.1
build 6.5.0
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "datasweet_formula",
"version": "1.1.3",
"version": "2.1.0",
"description": "This Kibana plugin allows calculated metrics on any standard kibana visualizations.",
"main": "index.js",
"kibana": {
Expand Down
1 change: 0 additions & 1 deletion public/agg_types/formula.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ export function AggTypesMetricsFormulaProvider(Private) {
],
getFormat: function (agg) {
const formatterId = agg.params.formatter;

if (!formatterId) {
return fieldFormats.getDefaultInstance('number');
}
Expand Down
8 changes: 3 additions & 5 deletions public/decorators/agg_configs.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ export function decorateVisAggConfigsProvider(Private) {
}

const toDslFn = AggConfigs.prototype.toDsl;
AggConfigs.prototype.toDsl = function () {
const isUsingFormula = !!this.vis.aggs.byTypeName.datasweet_formula;
AggConfigs.prototype.toDsl = function (hierarchical = false) {
const isUsingFormula = !!this.byTypeName.datasweet_formula;
const dsl = toDslFn.apply(this, arguments);
// Removes empty `datasweet_formula` aggs from dsl query if needed.
// This happens when `vis.isHierarchical()` returns true
return isUsingFormula && this.vis.isHierarchical() ? removeEmptyValues(dsl) : dsl;
return isUsingFormula && hierarchical ? removeEmptyValues(dsl) : dsl;
};
};
21 changes: 4 additions & 17 deletions public/decorators/agg_table.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import chrome from 'ui/chrome';
import { uiModules } from 'ui/modules';
import { TableTotalFormulaProvider } from './lib/apply_formula_total';
const appId = chrome.getApp().id;

// Defines the $scope.totalFunc allowed to perform the calculations
Expand All @@ -11,9 +12,10 @@ if (appId === 'kibana') {
.get('kibana')
.config(($provide) => {
// Decorates kbnAggTable default directive
$provide.decorator('kbnAggTableDirective', ($delegate, $controller) => {
$provide.decorator('kbnAggTableDirective', ($delegate, $controller, Private) => {
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 @@ -29,22 +31,7 @@ if (appId === 'kibana') {
const isTotalFunctionAllowed = $scope.showTotal && TOTAL_FUNCTIONS_ALLOWED.includes($scope.totalFunc);
const shouldApplyFormula = hasColumns && hasFormulas && isTotalFunctionAllowed;
if (!shouldApplyFormula) return;

// Overrides formattedColumn.total if needed
$scope.formattedColumns = table.columns.map(function (col, i) {
const formattedColumn = $scope.formattedColumns[i];
const agg = table.aggConfig(col);
const field = agg.getField();
const isFieldDate = field && field.type === 'date';
const hasFormula = agg.type && agg.type.name === 'datasweet_formula';
if (isFieldDate || !hasFormula) return formattedColumn;

const formatter = agg.fieldFormatter('text');
const totalRaw = table.totals[i][$scope.totalFunc];
formattedColumn.total = formatter(totalRaw);

return formattedColumn;
});
applyFormulaTotal(table, $scope);
});
};

Expand Down
16 changes: 8 additions & 8 deletions public/decorators/lib/apply_formula.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export function AggResponseFormulaProvider(Private) {
function extractSeriesAndFormulas(rows, cols) {
const res = { series: {}, formulas:[] };

each(cols, (c, i)=> {
const colIndex = i;
each(cols, c => {
const colId = c.id;
const columnGroupPrefix = c.columnGroup != null ? `colGroup${c.columnGroup}_` : '';
const key = columnGroupPrefix + varPrefix + c.aggConfig.id.replace('.', '_');

Expand All @@ -42,7 +42,7 @@ export function AggResponseFormulaProvider(Private) {
.replace(prefixRegExpr, columnGroupPrefix + varPrefix);
if (f.length > 0) {
res.formulas.push({
colIndex,
colId,
key,
compiled: (f.length > 0 ? parser.parse(f) : null)
});
Expand All @@ -53,7 +53,7 @@ export function AggResponseFormulaProvider(Private) {
// series.
else {
// TODO: analyze all formulas to build dependencies
res.series[key] = map(rows, r => rowValue(r[colIndex]));
res.series[key] = map(rows, r => rowValue(r[colId]));
}
});

Expand All @@ -66,7 +66,7 @@ export function AggResponseFormulaProvider(Private) {
let res = null;
try {
res = f.compiled.evaluate(datas.series);
computed[f.colIndex] = { value: res, isArray: isArray(res) };
computed[f.colId] = { value: res, isArray: isArray(res) };
} catch (e) {
res = null;
// console.log('ERROR', e);
Expand All @@ -89,12 +89,12 @@ export function AggResponseFormulaProvider(Private) {
if (!isEmpty(computed)) {
const isRowValue = isObject(table.rows[0][0]);
each(table.rows, (row, i) => {
each(computed, (data, colIndex) => {
each(computed, (data, colId) => {
const value = (data.isArray ? (isNil(data.value[i]) ? null : data.value[i]) : data.value);
if (isRowValue) {
row[colIndex].value = value;
row[colId].value = value;
} else {
row[colIndex] = value;
row[colId] = value;
}
});
});
Expand Down
123 changes: 42 additions & 81 deletions public/decorators/lib/apply_formula_total.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { each, find, get, isEmpty } from 'lodash';
import { each, get, isEmpty } from 'lodash';
import { FormulaParserProvider } from './formula_parser';

export function TableTotalFormulaProvider(Private) {
Expand All @@ -8,118 +8,79 @@ export function TableTotalFormulaProvider(Private) {
const varPrefix = 'agg';
const prefixRegExpr = new RegExp(varPrefix, 'g');

function hasFormulas(cols) {
return find(cols, 'aggConfig.type.name', aggTypeFormulaId) !== undefined;
}

function extractSeriesAndFormulas(totals, cols) {
function extractSeriesAndFormulas(table, $scope) {
const res = { series: {}, formulas:[] };

each(cols, (c, colIndex)=> {
each(table.columns, (c, i) => {
const formattedColumn = $scope.formattedColumns[i];
const colIndex = i;
const columnGroupPrefix = c.columnGroup != null ? `colGroup${c.columnGroup}_` : '';
const key = columnGroupPrefix + varPrefix + c.aggConfig.id.replace('.', '_');

// formula ?
// formula
if (c.aggConfig.type.name === aggTypeFormulaId) {
// reset total computed by kibana
formattedColumn.total = undefined;

// analyze formula
const f = get(c.aggConfig.params, 'formula', '')
.trim()
// Adds columnGroup to prefix
.replace(prefixRegExpr, columnGroupPrefix + varPrefix);
if (f.length > 0) {
res.formulas.push({
colIndex,
key,
formatter: c.aggConfig.fieldFormatter('text'),
compiled: (f.length > 0 ? parser.parse(f) : null)
});
}
res.series[key] = null;
}

// series
// not an metric
else if (isEmpty(formattedColumn.total)) {
res.series[key] = undefined;
}

// we recompute the total
else {
// TODO: analyze all formulas to build dependencies
res.series[key] = {
sum: totals[colIndex].sum,
avg: totals[colIndex].avg
};
function sum(tableRows) {
return _.reduce(tableRows, function (prev, curr) {
const v = get(curr[colIndex], 'value') || 0;
return prev + v;
}, 0);
}

switch ($scope.totalFunc) {
case 'sum':
res.series[key] = sum(table.rows);
break;
case 'avg':
res.series[key] = sum(table.rows) / table.rows.length;
break;
default:
res.series[key] = null;
break;
}
}
});

return res;
};

function compute(datas) {
const computed = {};
function compute(datas, $scope) {
each(datas.formulas, f => {
let res = null;
try {
const sumSeries = Object.keys(datas.series).reduce((obj, id) => {
obj[id] = datas.series[id] && datas.series[id].sum;
return obj;
}, {});

const avgSeries = Object.keys(datas.series).reduce((obj, id) => {
obj[id] = datas.series[id] && datas.series[id].avg;
return obj;
}, {});

res = {
sum: f.compiled.evaluate(sumSeries),
avg: f.compiled.evaluate(avgSeries)
};

computed[f.colIndex] = res;
} catch (e) {
res = null;
const res = f.compiled.evaluate(datas.series);
$scope.formattedColumns[f.colIndex].total = f.formatter(res);
}catch (e) {
// console.log('ERROR', e);
}
datas.series[f.key] = res;
});
return computed;
};

function applyColumnTotals(table, columns) {
table.totals = columns.reduce((arr, col, i) => {
const sum = tableRows => tableRows.reduce((prev, curr) => {
// some metrics return undefined for some of the values
// derivative is an example of this as it returns undefined in the first row
const v = get(curr[i], 'value');
if (v === undefined) return prev;
return prev + v;
}, 0);

arr[i] = {
sum: sum(table.rows),
avg: sum(table.rows) / table.rows.length
};
return arr;
}, []);
}

function mutate(table, columns) {
if (table.tables) {
table.tables.forEach(t => mutate(t, columns));
} else {
applyColumnTotals(table, columns);
const datas = extractSeriesAndFormulas(table.totals, columns);

// Compute and stocks
const computed = compute(datas);

// Applys
if (!isEmpty(computed)) {
each(computed, (data, colIndex) => {
table.totals[colIndex] = {
sum: data.sum,
avg: data.avg
};
});
}
}
};

return function apply(columns, resp) {
if (columns.length === 0 || resp.length === 0 || !hasFormulas(columns)) return;
mutate(resp, columns);
return function apply(table, $scope) {
const datas = extractSeriesAndFormulas(table, $scope);
compute(datas, $scope);
};
};
Loading

0 comments on commit a354db0

Please sign in to comment.