@johtaniの日記 2nd

@johtani ‘s blog 2nd edition

Analyze UIとKibanaのプラグインの作成方法(第3回)

第2回から少し間が空いてしまいましたが、templateで作成したプラグインのディレクトリ構成とどういう流れでデータがやり取りされるかについてみていきます。 (2018/02月時点で作成したディレクトリ構成にしたがって説明します) ちなみに、JavaScriptの優れた開発者ではないので、誤解している点や、効率の悪い書き方などがあるかもしれません。見つけた場合は、連絡をいただければと思います。

では、まずは作成したディレクトリ構成についてみていきましょう。

ディレクトリ構成

simple-sample-kibana-pluginがプラグインのプロジェクトのトップディレクトリになります。このディレクトリに次のような構成でサブディレクトリが存在します(なお、画像はIntelliJに取り込んだ後のディレクトリになっているので、.imlなど、不要なファイル/ディレクトリが存在しています)。

主要なディレクトリ、ファイルについて簡単に一覧で説明します(順不同)。

ファイル/ディレクトリ名 説明
index.js プラグインの本体。Kibanaはこのファイルのオブジェクトを読み込みプラグインを起動。設定などの読み込みもこちら。
package.json npm/yarnのパッケージに関する情報を定義するファイル
README.md README。プラグインの説明などを記載する。インストール方法なども記載すると便利
public ブラウザ側に配布されるプログラムや画像一式
public/less/main.less LESS用のファイル。アプリ固有のスタイルなどを記載
public/app.js ブラウザ側で読み込まれるプラグインのモジュールなど。
public/template/index.html HTMLのテンプレート。ブラウザ上での描画に利用
server/routes Kibanaサーバー側で動作するプラグイン。hapi.jsを利用してREST APIを実装する

重要なファイルについて少しだけ説明します。

package.json

npmやyarnでビルドなどをするときに使用するパッケージ情報を記載するためのファイルです。 プラグインの名前、バージョン、説明などを記載します。 Kibanaのバージョンについてもこちらで管理します。この情報を また、ライブラリなどの依存関係についてもこちらで記載しています。 以下、抜粋。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "name": "simple-sample-kibana-plugin",
  "version": "0.0.0",
  "description": "Sample plugin for explaining how to make kibana app",
  "main": "index.js",
  "kibana": {
    "version": "6.2.1",
    "templateVersion": "7.2.4"
  },
  "scripts": {
    "lint": "eslint **/*.js",
...
  },
  "devDependencies": {
    "@elastic/eslint-config-kibana": "^0.14.0",
    "@elastic/eslint-import-resolver-kibana": "^0.9.0",
    "@elastic/plugin-helpers": "^7.1.3",
...
    "expect.js": "^0.3.1"
  }
}

ちなみに私は、versionなどをリリースするたびに変更しています。

index.js

最初にKibanaに読み込まれるオブジェクトになります。 Kibanaのアプリの名前や、必要なモジュールなどを記載します。

また、kibana.ymlから設定など読み込む処理なども書くことができます。

2行目のexampleRouteはサーバー側のAPIとして利用するhapi.js用のファイルのパスになります。

uiExportsはこのアプリの画面に関する設定などの記載になります。 appの部分が実際にアプリの情報で、 mainがあとで説明するこのプラグインのUIのためのJavaScriptファイル(public/app.js)になります。mainですので、最初に読み込まれる処理が記載されているものを指定します。app.jsというファイル名を変更する場合は、こちらのappの部分を変更したファイルに合わせましょう。

config(Joi)の関数が設定ファイルの読み込みなどの処理を記載する場所です。

init(server, options)の関数が初期化処理を記載する場所になります。 このサンプルアプリでは、2行目のimportで読み込んだhapi.js用のファイルの関数を呼び出しています。引数で渡しているserverがhapi.jsのserverオブジェクトになります。 routeメソッドを使用して作成しているプラグイン用のREST APIを追加しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import { resolve } from 'path';
import exampleRoute from './server/routes/example';

export default function (kibana) {
  return new kibana.Plugin({
    require: ['elasticsearch'],
    name: 'simple-sample-kibana-plugin',
    uiExports: {

      app: {
        title: 'Simple Sample Kibana Plugin',
        description: 'Sample plugin for explaining how to make kibana app',
        main: 'plugins/simple-sample-kibana-plugin/app'
      },

...
    },

    config(Joi) {
      return Joi.object({
        enabled: Joi.boolean().default(true),
      }).default();
    },

    init(server, options) {
      // Add server routes and initialize the plugin here
      exampleRoute(server);
    }
  });
};

public/app.js

画面用のモジュールです。 uiRoutesという機能を使用して、アプリの呼び出しURLを定義します。テンプレートで作成したばかりの場合は、/というURLが追加されるのみです。

実際に画面を表示する際に動くコントローラーの部分はその下の uiModules.controllerに指定してあるfunctionが画面描画の 処理を書く部分になります。 templateで作成したプラグインでは、”title”など表示に必要なデータを$scopeというオブジェクトに詰め込んでいます。 これはAngularJS(1系)でのモデルオブジェクトになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import moment from 'moment';
import { uiModules } from 'ui/modules';
import uiRoutes from 'ui/routes';

import 'ui/autoload/styles';
import './less/main.less';
import template from './templates/index.html';

uiRoutes.enable();
uiRoutes
  .when('/', {
    template,
    resolve: {
...
    }
  });

uiModules
  .get('app/simple-sample-kibana-plugin', [])
  .controller('simpleSampleKibanaPluginHelloWorld', function ($scope, $route, $interval) {
    $scope.title = 'Simple Sample Kibana Plugin';
    $scope.description = 'Sample plugin for explaining how to make kibana app';
...
    $scope.$watch('$destroy', unsubscribe);
  });

server/routes/example.js

hapi.jsというNode.jsのためのサーバーフレームワークです。 このフレームワークをKibanaは使っており、Kibanaのサーバーとブラウザとのやり取りに使用するREST APIを記述するために使用しています。 例えば、Elasticsearchとのやり取りを実際に行うAPIなどをこのREST API内部で記述します。

1
2
3
4
5
6
7
8
9
10
11
export default function (server) {

  server.route({
    path: '/api/simple-sample-kibana-plugin/example',
    method: 'GET',
    handler(req, reply) {
      reply({ time: (new Date()).toISOString() });
    }
  });

}

pathの部分がブラウザ側からアクセスするURLになります。 実際にElasticsearchとやり取りする処理の書き方については、次回の記事で説明します。

アーキテクチャ(簡易版)

ざっくりですが、ファイルやディレクトリについて説明しました。 簡単なデータのやり取りについての流れを説明します。

Kibana自体はNode.jsで実装されサーバーとして動作していますが、ブラウザでアクセスすることで画面を描画しています。 簡単なコンポーネントを並べるとデータのやり取りはこのような形です。

すごく簡易で大雑把な絵ですが。。。

実際のプラグインとしては大きく、2つの処理があります。

  • ブラウザ上の処理
    • クリックなどのイベント処理
    • HTMLなどのレンダリング処理
  • Kibanaサーバー上の処理(Elasticsearchなどとの通信が必要な場合)
    • 外部との通信処理
    • ブラウザ上では重い処理

絵に記載しましたが、ブラウザ上の処理についてはAngularJSが主なフレームワークで、サーバー上の処理についてはhapi.jsがフレームワークとなっています。

まとめ

ということで、今回はディレクトリ構造とファイルの説明、どういったフレームワークが使われ、データのやり取りがどのように行われているか説明しました。

次回からは、実際に私が作成したAnalyze UIを元にElasticsearchとのデータのやり取りなどについて紹介していきます。

Comments