Создаем пользовательскую визуализацию в Kibana 7.1.1

Предисловие
В процессе разработки наша команда столкнулась с тем, что не так уж и много инструкций или “туториалов” по созданию собственной визуализации.
Конечно, существует официальная документация по разработке визуализации, но, как мне кажется, она недостаточно удобна и подробна.
В этой статье я опишу процесс создания визуализации как отдельного плагина на kibana 7.1.*.
Создаем плагин
Для этого переходим в папку kibana/plugins
Создаем папку для нашего плагина.
В нашей папке создаем:
- package.json
- index.js
- public (директория)
В package.json:
- name– имя плагина.
- version– версия Kibana, в которой будет работать плагин.
- description– описание.
- main– имя главного файла плагина.
{
    "name": "SM_tutorial_vis",
    "version": "7.1.1",
    "description": "SM_tutorial_vis",
    "main": "index.js"
}В index.js:
Мы объявляем новый плагин Kibana, который будет являться визуализацией. Не забываем указывать правильное имя!
export default function (kibana) {
    return new kibana.Plugin({
        uiExports: {
            visTypes: [
            'plugins/SM_tutorial_vis/SM_tutorial_vis'
            ]
        },
    });
}Создаем визуализацию
Переходим в папку /public.
Cоздаем необходимые файлы:
- SM_tutorial_vis.js
- SM_tutorial_vis_controller.js
- SM_tutorial_vis_editor.html
- SM_tutorial_vis.less
SM_tutorial_vis.js
В этом файле создается сама визуализация, а также прописываются необходимые настройки и параметры.
Делаем необходимые импорты:
import { VisFactoryProvider } from 'ui/vis/vis_factory';
import { VisTypesRegistryProvider } from 'ui/registry/vis_types';
import { Schemas } from 'ui/vis/editors/default/schemas';
import './SM_tutorial_vis.less';
import optionsTemplate from './SM_tutorial_vis_editor.html';
import { SmTutorialController } from './SM_tutorial_vis_controller';Опишем функцию SmTutorialVis, в которой создадим визуализацию, с помощью объекта класса VisFactoryProvider:
function SmTutorialVis(Private) {
  const VisFactory = Private(VisFactoryProvider);
  return VisFactory.createBaseVisualization({ //Создаем визуализацию
         ...
  });
}Существует несколько VisFactoryProvider помимо BaseVisualisation:
- AngularVisType
- ReactVisType
- VislibVisType
Далее опишем нашу визуализацию:
  return VisFactory.createBaseVisualization({
    name: 'SM_tutorial_vis', // Имя визуализации
    type: 'metric', // Тип визуализации
    title: 'SM tutorial', // Заголовок, который будет написан в kibana
    icon: 'smVis', // Иконка EUI
    description: 'This is visualisation from volgablob tutorial', // Описание
    visualization: SmTutorialController, //Класс контроллер, в котором опишем логикуДалее указывается словарь visConfig, в котором можем задать любые параметры, которые нам пригодятся в контроллере. Эти параметры будут проинициализированы нашими значениями при создании визуализации.
Они также могут быть использованы во вкладке Options в самой Kibana при создании визуализации.
visConfig: {
      defaults: {
        metricTitle: null,
        fontSize: 40,
        titleIndicator: 'SM title',
        volgablob: 'VolgaBlob'
      },
    },Следующим указываем словарь editorConfig, в котором можно указать шаблон вкладки Options и указать схему:
editorConfig: {
      optionsTemplate: optionsTemplate, //html шаблон вкладки Options
      //Схема, которая будет использована для визуализации
      //Можно указать metrics, buckets и тд
      schemas: new Schemas([
        {
          group: 'metrics',
          name: 'metric',
          title: 'Metric',
          aggFilter: ['max'], //Фильтр доступных агрегаций. Можно испольвать '!avg', тогда будут все, кроме avg
          min: 1, //Минимальное количество метрик
          max: 1, //Максимальное количество метрик
          defaults: [
            { type: 'max', schema: 'metric' } //Стандартное значение метрики
          ]
        }
      ])
    },
  });
}И последняя часть в этом файле, но не по значимости!
Регистрируем нашу визуализацию c помощью VisTypesRegistryProvider. 
Как параметр, передаем созданную нами функцию SmTutorialVis:
VisTypesRegistryProvider.register(SmTutorialVis);
Логика визуализации
SM_tutorial_vis_controller.js
- Создаем класс, который мы описывали как контроллер в SM_tutorial_vis.js
Начинаем с конструктора:
class SmTutorialController {
    constructor(el, vis) {
      this.el = el;
      this.vis = vis;
      this.container = document.createElement('div');
      this.container.className = 'smTutorial';
      this.el.appendChild(this.container);
      this.metricValue = null;
    }Опишем метод render.
Это основной метод, который нарисует нашу визуализацию. В этом примере мы создадим обычный div, внутри которого будет рендерится визуализация.
render(visData, status) {
      this.container.innerHTML = '';
      const table = visData;
      const metrics = [];
      let bucketAgg;
      table.columns.forEach((column, i) => {
        table.rows.forEach(row => {
          const value = row[i];
          metrics.push({
            title: bucketAgg ? `${row[0]} ${column.title}` : column.title,
            value: row[column['id']],
            formattedValue: column.aggConfig ? column.aggConfig.fieldFormatter('text')(value) : value,
            bucketValue: bucketAgg ? row[0] : null,
            aggConfig: column.aggConfig
          });
        });
      });Здесь стоит обратить внимание на параметр visData. Там хранится вся необходимая информация о метрике, а именно: значение, тип агрегации, пользовательский лейбл и тд. В зависимости от этих данных мы и будем вставлять нашу html разметку.
Также здесь мы формируем массив metrics для удобства работы с данными далее.
Давайте пройдемся по массиву metrics и создадим блоки div для отображения нашей визуализации:
metrics.forEach(metric => {
        this.metricValue = metric.value;
        var title = metric.aggConfig.params.customLabel;
        //Если изменено название и оно не пустое
        if(!title) {
          title = "SM tutorial";
        }
        const metricDiv = document.createElement(`div`);
        ...
        metricDiv.innerHTML = `<div class="icon">${icon}</div> <div class="desc">${title}</div>`;
});И не забываем вернуть Promise:
return new Promise(resolve => {
        resolve('Done rendering');
      });
}Добавляем метод destroy:
destroy() {
      this.el.innerHTML = '';
      console.log('Destroying');
    }
};Экспортируем наш класс:
export { SmTutorialController };Разметка вкладки options
SM_tutorial_vis_editor_editor.html
Здесь мы можем отредактировать вкладку Options, чтобы сделать поля для дополнительных настроек визуализации.
<div class="form-group"> <label> Размер шрифта</label> <input type="number" ng-model="vis.params.fontSize" class="form-control"/> </div> <div class="form-group"> <label>Заголовок индикатора</label> <input type="text" ng-model="vis.params.titleIndicator" class="form-control"/> </div>
vis.params.* – поля, которые мы задавали в SM_tutorial_vis.js в словаре visConfig.
Cтили
SM_tutorial_vis.less
Простой less файл, который позволит добавить нашей визуализации немного красоты и каскадных таблиц :).
После этого можно запускать kibana и наслаждаться новой визуализацией!