KibanaのAPIにrubyからアクセスする

# 概要
Kibanaに対してRubyからVisualizeやDashboardを作ったりしたかったので書いた

# コード

``` ruby
class KibanaClient
  def self.call method, path, data
    endpoint = "http://localhost:5602#{path}"

    authorization = 'Basic ' + Base64.encode64("elastic:changeme").chomp
    headers = {
      authorization: authorization,
      "kbn-xsrf": "kibana",
      content_type: :json,
      accept: :json
    }

    response = nil
    begin
      response = RestClient.send(method.to_s, endpoint, data.to_json, headers)
    rescue => e
      puts e.response
      response = e.response
    end

    response
  end
end

```

## Tips

* kbn-xsrfが無いと弾かれる
* Basic認証はKibanaのユーザで行う
* リクエストに必要なJSONはKibanaのSaved ObjectsからInspectすると見れる
* Saved ObjectsのExportで更に詳しく見れる

# サンプル

``` ruby

class KibanaClient
  def self.call method, path, data
    endpoint = "http://localhost:5602#{path}"

    authorization = 'Basic ' + Base64.encode64("elastic:changeme").chomp
    headers = {
      authorization: authorization,
      "kbn-xsrf": "kibana",
      content_type: :json,
      accept: :json
    }

    response = nil
    begin
      response = RestClient.send(method.to_s, endpoint, data.to_json, headers)
    rescue => e
      puts e.response
      response = e.response
    end

    response
  end

  def self.gen_metric id, index_pattern, title, references = nil, query = nil
    references = [
      {
        "id": index_pattern,
        "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
        "type": "index-pattern"
      }
    ] if references.blank?
    query = {
      "query": {
        "query": "",
        "language": "kuery"
      }, 
      "filter": [], 
      "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index"
    } if query.blank?

    state = {
      "title": title,
      "type": "metric",
      "params": {
        "metric": {
          "percentageMode": false,
          "useRanges": false,
          "colorSchema": "Green to Red",
          "metricColorMode": "None",
          "colorsRange": [
            {
              "type": "range",
              "from": 0,
              "to": 10000
            }
          ],
          "labels": {
            "show": false
          },
          "invertColors": false,
          "style": {
            "bgFill": "#000",
            "bgColor": false,
            "labelColor": false,
            "subText": "",
            "fontSize": 60
          }
        },
        "dimensions": {
          "metrics": [
            {
              "type": "vis_dimension",
              "accessor": 0,
              "format": {
                "id": "number",
                "params": {}
              }
            }
          ]
        },
        "addTooltip": true,
        "addLegend": false,
        "type": "metric"
      },
      "aggs": [
        {
          "id": "1",
          "enabled": true,
          "type": "count",
          "schema": "metric",
          "params": {}
        }
      ]
    }

    json = {
      "attributes": {
        "description": "",
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": query.to_json
        },
        "title": title,
        "uiStateJSON": "{}",
        "visState": state.to_json
      },
      "references": references
    }

    api_path = "/api/saved_objects/visualization/#{id}?overwrite=true"

    response = KibanaClient.call(:post, api_path, json)

    response
  end

  def self.gen_pie id, index_pattern, title, field, references = nil, query = nil
    references = [
      {
        "id": index_pattern,
        "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
        "type": "index-pattern"
      }
    ] if references.blank?
    query = {
      "query": {
        "query": "",
        "language": "kuery"
      }, 
      "filter": [], 
      "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index"
    } if query.blank?

    state = {
      "title": title,
      "type": "pie",
      "params": {
        "type": "pie",
        "addTooltip": true,
        "addLegend": true,
        "legendPosition": "top",
        "isDonut": true,
        "labels": {
          "show": false,
          "values": true,
          "last_level": true,
          "truncate": 100
        },
        "dimensions": {
          "metric": {
            "accessor": 1,
            "format": {
              "id": "number"
            },
            "params": {},
            "aggType": "count"
          },
          "buckets": [
            {
              "accessor": 0,
              "format": {
                "id": "terms",
                "params": {
                  "id": "string",
                  "otherBucketLabel": "Other",
                  "missingBucketLabel": "Missing"
                }
              },
              "params": {},
              "aggType": "terms"
            }
          ]
        }
      },
      "aggs": [
        {
          "id": "1",
          "enabled": true,
          "type": "count",
          "schema": "metric",
          "params": {}
        },
        {
          "id": "2",
          "enabled": true,
          "type": "terms",
          "schema": "segment",
          "params": {
            "field": field,
            "orderBy": "1",
            "order": "desc",
            "size": 5,
            "otherBucket": true,
            "otherBucketLabel": "Other",
            "missingBucket": false,
            "missingBucketLabel": "Missing"
          }
        }
      ]
    }
    
    json = {
      "attributes": {
        "description": "",
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": query.to_json
        },
        "title": title,
        "uiStateJSON": "{}",
        "visState": state.to_json
      },
      "references": references
    }

    api_path = "/api/saved_objects/visualization/#{id}?overwrite=true"

    response = KibanaClient.call(:post, api_path, json)

    response
  end

  def self.gen_graph id, index_pattern, title, type, params, aggs, references = nil, query = nil
    references = [
      {
        "id": index_pattern,
        "name": "kibanaSavedObjectMeta.searchSourceJSON.index",
        "type": "index-pattern"
      }
    ] if references.blank?
    query = {
      "query": {
        "query": "",
        "language": "kuery"
      }, 
      "filter": [], 
      "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.index"
    } if query.blank?

    state = {
      "title": title,
      "type": type,
      "params": params,
      "aggs": aggs
    }
    
    json = {
      "attributes": {
        "description": "",
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": query.to_json
        },
        "title": title,
        "uiStateJSON": "{}",
        "visState": state.to_json
      },
      "references": references
    }

    api_path = "/api/saved_objects/visualization/#{id}?overwrite=true"

    response = KibanaClient.call(:post, api_path, json)

    response
  end

  def self.gen_dashboard id, title, panels, references, options = {}, query = nil
    query = {
      "query": {
        "query": "",
        "language": "kuery"
      }, 
      "filter": []
    } if query.blank?

    puts id
    puts title
    puts panels
    puts references
    puts options
    puts query

    json = {
      "attributes": {
        "description": "",
        "hits": 0,
        "kibanaSavedObjectMeta": {
          "searchSourceJSON": query.to_json
        },
        "optionsJSON": options.to_json,
        "panelsJSON": panels.to_json,
        "timeRestore": false,
        "title": title
      },
      "references": references
    }

    puts json 

    api_path = "/api/saved_objects/dashboard/#{id}?overwrite=true"

    response = KibanaClient.call(:post, api_path, json)

    response
  end
end

```

Generatorから

``` ruby
response = KibanaClient.gen_graph("test_graph", "index_name", "タイトル", "area", params, aggs, references, query)
```

各変数はKibanaでグラフを作って、Inspectからテンプレ化すると楽