Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
olsn committed Oct 30, 2016
1 parent 507ff17 commit e755ce9
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Kibana Extended Metric Plugin

This is a plugin for [Kibana 5.0.0+](https://www.elastic.co/products/kibana).
It is based on the core Metric-Plugin but gives you the ability to output custom aggregates on metric-results by using custom formula and/or JavaScript.

![image](img/demo.gif)

## Installation

```sh
$ ./bin/kibana-plugin install https://github.com/ommsolutions/kibana_ext_metrics_vis/archive/0.1.0.zip
```

### Manual

Extract the ZIP into a new folder in your `kibana/plugins`-directory.

## Uninstall

Simply delete that folder and restart kibana.
Binary file added img/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

Object.defineProperty(exports, '__esModule', {
value: true
});

exports['default'] = function (kibana) {
return new kibana.Plugin({
uiExports: {
visTypes: ['plugins/extended_metric_vis/extended_metric_vis']
}

});
};

;
module.exports = exports['default'];
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "extended_metric_vis",
"version": "kibana",
"author": "Olaf Horstmann <[email protected]>",
"website": "http://www.omm-solutions.de"
}
6 changes: 6 additions & 0 deletions public/extended_metric_vis.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div ng-controller="KbnExtendedMetricVisController" class="output-vis">
<div class="output-container" ng-repeat="output in vis.params.outputs | filter:{enabled:true}">
<div class="output-value" ng-style="{'font-size': vis.params.fontSize+'pt'}">{{output.value}}</div>
<div>{{output.label}}</div>
</div>
</div>
56 changes: 56 additions & 0 deletions public/extended_metric_vis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'plugins/extended_metric_vis/extended_metric_vis.less';
import 'plugins/extended_metric_vis/extended_metric_vis_controller';
import TemplateVisTypeTemplateVisTypeProvider from 'ui/template_vis_type/template_vis_type';
import VisSchemasProvider from 'ui/vis/schemas';
import extendedMetricVisTemplate from 'plugins/extended_metric_vis/extended_metric_vis.html';
import metricVisParamsTemplate from 'plugins/extended_metric_vis/extended_metric_vis_params.html';
// we need to load the css ourselves

// we also need to load the controller and used by the template

// register the provider with the visTypes registry
require('ui/registry/vis_types').register(ExtendedMetricVisProvider);

function ExtendedMetricVisProvider(Private) {
const TemplateVisType = Private(TemplateVisTypeTemplateVisTypeProvider);
const Schemas = Private(VisSchemasProvider);

// return the visType object, which kibana will use to display and configure new
// Vis object of this type.
return new TemplateVisType({
name: 'extended_metric',
title: 'Extended Metric',
description: 'Based on the core Metric-Plugin but gives you the ability' +
'to output custom aggregates on metric-results.',
icon: 'fa-calculator',
template: extendedMetricVisTemplate,
params: {
defaults: {
handleNoResults: true,
fontSize: 60,
outputs: [
{
formula: 'metrics[0].value * metrics[0].value',
label: 'Count squared',
enabled: true
}
]
},
editor: metricVisParamsTemplate
},
schemas: new Schemas([
{
group: 'metrics',
name: 'metric',
title: 'Metric',
min: 1,
defaults: [
{ type: 'count', schema: 'metric' }
]
}
])
});
}

// export the provider so that the visType can be required with Private()
export default ExtendedMetricVisProvider;
35 changes: 35 additions & 0 deletions public/extended_metric_vis.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import (reference) "~ui/styles/mixins.less";

.output-vis {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;

.output-value {
font-weight: bold;
.ellipsis();
}

.output-container {
text-align: center;
}
}

.vis-editor-agg-header.output {
.vis-editor-agg-header-title {
-webkit-box-flex: 1;
-webkit-flex: 1 1 auto;
-ms-flex: 1 1 auto;
flex: 1 0 auto;
padding-right: 5px;
}

.vis-editor-agg-header-description {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
86 changes: 86 additions & 0 deletions public/extended_metric_vis_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import _ from 'lodash';
import AggResponseTabifyTabifyProvider from 'ui/agg_response/tabify/tabify';
import uiModules from 'ui/modules';
const module = uiModules.get('kibana/extended_metric_vis', ['kibana']);

module.controller('KbnExtendedMetricVisController', function ($scope, Private) {
const tabifyAggResponse = Private(AggResponseTabifyTabifyProvider);
const metrics = $scope.metrics = [];
const calcOutputs = $scope.calcOutputs = [];

function isInvalid(val) {
return _.isUndefined(val) || _.isNull(val) || _.isNaN(val);
}

function updateOutputs() {
$scope.vis.params.outputs.forEach(function (output) {
try {
const func = Function("metrics", "return " + output.formula);
output.value = func(metrics) || "?";
} catch (e) {
output.value = '?';
}
});
}

$scope.processTableGroups = function (tableGroups) {
tableGroups.tables.forEach(function (table) {
table.columns.forEach(function (column, i) {
const fieldFormatter = table.aggConfig(column).fieldFormatter();
let value = table.rows[0][i];
let formattedValue = isInvalid(value) ? '?' : fieldFormatter(value);

const metric = {
label: column.title,
value: value,
formattedValue: formattedValue
};
metrics.push(metric);
metrics[column.title] = metric;
});
});

updateOutputs();
};

// watches
$scope.$watch('esResponse', function (resp) {
if (resp) {
calcOutputs.length = 0;
metrics.length = 0;
for (let key in metrics) {
if (metrics.hasOwnProperty(key)) {
delete metrics[key];
}
}
$scope.processTableGroups(tabifyAggResponse($scope.vis, resp));
}
});

$scope.$watchCollection('vis.params.outputs', updateOutputs);
});

module.controller('ExtendedMetricEditorController', function ($scope) {
// Output Related Methods:
$scope.addOutput = function (outputs) {
outputs.push({
formula: 'metrics[0].value * metrics[0].value',
label: 'Count squared',
enabled: true
});
};

$scope.removeOuput = function (output, outputs) {
if (outputs.length === 1) {
return;
}
const index = outputs.indexOf(output);
if (index >= 0) {
outputs.splice(index, 1);
}

if (outputs.length === 1) {
outputs[0].enabled = true;
}
};
});
86 changes: 86 additions & 0 deletions public/extended_metric_vis_params.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<div ng-controller="ExtendedMetricEditorController as paramCtrl"
style="width: 100%;">
<div draggable-container="$parent.vis.params.outputs"
style="width: 100%;">
<div ng-repeat="output in $parent.vis.params.outputs"
draggable-item="output"
style="width: 100%;">
<div class="vis-editor-agg-header output" style="width: 100%;">
<!-- open/close editor -->
<button aria-label="{{editorOpen ? 'Close' : 'Open'}} Editor"
ng-click="editorOpen = !editorOpen"
type="button"
class="btn btn-primary btn-xs vis-editor-agg-header-toggle">
<i aria-hidden="true" ng-class="{ 'fa-caret-down': editorOpen, 'fa-caret-right': !editorOpen }" class="fa"></i>
</button>

<!-- title -->
<span class="vis-editor-agg-header-title">
Output
</span>

<!-- description -->
<span ng-if="!editorOpen" class="vis-editor-agg-header-description">
{{output.label || output.formula}}
</span>

<!-- buttons -->
<div ng-show="vis.params.outputs.length > 1" class="vis-editor-agg-header-controls btn-group">
<button
ng-click="output.enabled = !output.enabled"
aria-label="{{output.enabled ? 'Disable' : 'Enable'}} Output"
tooltip="{{output.enabled ? 'Disable' : 'Enable'}} Output"
tooltip-append-to-body="true"
type="button" class="btn btn-xs">
<i aria-hidden="true"
class="fa"
ng-class="{'fa-toggle-off': !output.enabled, 'fa-toggle-on': output.enabled}"></i>
</button>

<button draggable-handle
aria-label="Modify Priority by Dragging"
tooltip="Modify Priority by Dragging"
tooltip-append-to-body="true"
type="button"
class="btn btn-xs gu-handle">
<i aria-hidden="true" class="fa fa-arrows-v"></i>
</button>

<button
aria-label="Remove Output" ng-click="removeOuput(output, $parent.vis.params.outputs)"
tooltip="Remove Output"
tooltip-append-to-body="true"
type="button" class="btn btn-xs btn-danger">
<i aria-hidden="true" class="fa fa-times"></i>
</button>
</div>
</div>

<!-- editor -->
<div ng-if="editorOpen">
<div class="form-group">
<label>Formula</label>
<input type="text" ng-model="output.formula" class="form-control" />
</div>
<div class="form-group">
<label>Custom Label</label>
<div>
<input type="text" ng-model="output.label" class="form-control ng-pristine ng-untouched ng-valid">
</div>
</div>
</div>
</div>
</div>
<div ng-click="addOutput($parent.vis.params.outputs)" class="vis-editor-agg-wide-btn">
<div>
<div class="btn btn-sm btn-primary">
Add output
</div>
</div>
</div>
</div>
<hr>
<div class="form-group">
<label>Font Size - {{ vis.params.fontSize }}pt</label>
<input type="range" ng-model="vis.params.fontSize" class="form-control" min="12" max="120" />
</div>

0 comments on commit e755ce9

Please sign in to comment.