たごもりすメモ

コードとかその他の話とか。

GrowthForecastのJSON APIおよびクライアントライブラリについて

雪にかこまれた温泉宿からこんちには。
クライアントライブラリを書いてからblogエントリにしようと思ったらこんな素敵エントリを書かれてしまった、くやしい……!

GrowthForecastでAPI使って複合グラフ作ったり、グラフの色を変えたりしてみた - mikedaの日記

みんな大好き GrowthForecast ですが、じつに簡単にグラフを作れてしまえるので調子に乗って作りまくった挙句、複合グラフを作る手作業で死んだり色を揃える作業で死んだりそもそも無いといけないグラフが本当にあるのか確認するのが面倒でうっちゃってたりするようなことがあります。必ずあるはずです。ですよねー。あるある。

ということでグラフの存在を確認したりグラフ設定を変更したりそもそも無ければ作ったり、という作業をコードからやりたかったので、ひとそろい作った。とりあえず GrowthForecast に JSON API を作り、それからクライアントライブラリも。クライアントライブラリはPerlRubyでほぼ同機能のものを。

Net::GrowthForecast - A client library for awesome visualization tool GrowthForecast - metacpan.org
growthforecast | RubyGems.org | your community gem host

GrowthForecast本体のドキュメントAPIの記述を追加してないなー。きっとkazeburoさんがやってくれる……。(もしくは書くのでリポジトリか何か教えてください > kazeburo)

なお、GrowthForecastのコードが相応に新しくないと JSON APIが存在しないのでクライアントライブラリも使えません。超具体的には、現在のリリースの最新版である v0.33 もしくはそれ以降のものを使用しましょう。

ちなみに最初はクライアントライブラリをGrowthForecastに手を入れない形で(HTMLをスクレイピングして)作ろうとしてたんだけど、あまりにいろいろつらくて途中で一度全部捨てた。一度全部捨ててからここまでの完成のほうがはるかに早かった……。

JSON API

簡単に書くとこんな感じ。なお対象が存在しない場合、単数が返るcallについては404が、リストが返るcallについては空リストが返る。*1

  • GET /json/list/graph
    • 存在するグラフの一覧を取得する、複合グラフは含まない
    • service_name, section_name, graph_name および id (graphで一意) の組のリスト
  • GET /json/list/complex
    • 存在する複合グラフの一覧を取得する
    • service_name, section_name, graph_name および id (complexで一意)*2 の組のリスト
  • GET /json/graph/:id および /json/complex/:id
    • グラフの詳細情報を返す、この表現形式が edit/delete でも使われる
  • GET /json/list/all
    • 全グラフ(複合グラフを含む)の詳細情報のリストを返す
  • POST /json/edit/graph/:id および /json/edit/complex/:id
    • 取得したグラフオブジェクトを必要に応じて変更し上記URLにPOSTする(オブジェクトをJSONシリアライズしてcontent bodyにする)とグラフが変更される
  • POST /json/create/complex
    • 複合グラフを作成する、設定内容は複合グラフの詳細情報形式で content body (JSON) で送る

いくつか注意点があります。

  • 複合グラフかそうでないかはオブジェクトのcomplex属性を見る
  • 今のところバリデーションがほぼ行われてない
    • ので type とか gmode とかに既定値以外の変な値を突っ込むとおかしな表示になるかも、というか、なる
  • グラフの詳細情報の形式と GET /api/:service/:section/:graph で返るJSONの形式は異なる
    • 正確に言うと情報量は同じだが、詳細情報形式では不要な属性が取り除かれたり complex フラグがあったりする
    • また GET /api/:service/:section/:graph では複合グラフの情報はとれない
クライアントライブラリ

JSON APIはシンプルな実装を心掛けたので、それだけだと以下のように少し使いにくいです。

  • グラフ作成は複合グラフしかない
    • 通常のグラフは値をポストすれば作られるしね
  • グラフ削除が複雑
    • 複合グラフの削除は対象を id で指定するが通常のグラフは service/section/graph で指定する

なので、クライアントライブラリを作りました。とりあえず必要な人が多そうな*3PerlRubyで。CPANrubygems.orgに上げてあります。
主に以下のような機能があります。機能名からなんとなく察してください。グラフ追加は面倒なので省略……。詳しくは各ライブラリのREADMEにけっこうちゃんと書いてあります。

use Net::GrowthForecast;
my $gf = Net::GrowthForecast->new(host => 'my.growthforecast.host.local', port => 80);
$gf->graphs();
$gf->complexes();
$gf->all();
$gf->tree()->{servicename}->{sectionname}->{graphname}; #=> detail of graph/complex by hashref

$gf->graph( $graph1->{id} );
$gf->complex( $complex1->{id} );

$gf->by_name('service', 'section', 'graph');

$graph->{description} = 'update now';
$gf->edit( $graph );

$gf->delete( $graph );

rubyだとこんな。

require 'growthforecast'
gf = GrowthForecast.new('my.growthforecast.host.local', 80)
gf.graphs()         #=> instance of GrowthForecast::Path (has .id, .service_name, .section_name, .graph_name, .complex?)
gf.complexes()   #=> instance of GrowthForecast::Path
gf.all()                #=> array of instance of GrowthForecast::(Graph|Complex)
gf.tree()['servicename']['sectionname']['graphname']

gf.graph( graph1.id ) #=> GrowthForecast::Graph
gf.complex( complex1.id) #=> GrowthForecast::Complex

gf.by_name('service', 'section', 'graph')

graph.description = 'update now'
gf.edit( graph )

gf.delete( graph )

まあそこそこラクに使えるのではないでしょうか!

これから

現状でもちょっとコード書かないといけないので、更にもうちょっと実装を進めて以下のようなツールを作るつもり。

  • 定義をYAMLから読む
  • 定義と引数に従って「GrowthForecast上に存在するはずのグラフ(のセット)」を確認する
    • なければ作る
    • あっても設定が違えば修正する
    • 設定も同じものがあれば何もしない
  • 引数としてサービス名を与えると、そのサービスに必要なグラフがひとそろい、コマンド一発で揃うような感じ

これはRuby版のクライアントライブラリに添付する形で作る。前はPerl版で作ろうと思ってたんだけどYAML実装が true/false を扱えないのを確認して挫折した。
1月中には作りたいなー。

*1:ように作るのが普通だが /json/graph/:id および /json/complex/:id で忘れてたので v0.33 では500になってる、修正をpullreq済み

*2:graphとは重複する

*3:というか自分で使いそうな