ラベル groovy の投稿を表示しています。 すべての投稿を表示
ラベル groovy の投稿を表示しています。 すべての投稿を表示

2011年12月4日日曜日

Groovy+HttpClient で Redmine の wiki 更新

Redmine の wiki を、HTTP のクライアントプログラムで更新する方法を考えてみた。言語は何でもいいが、ここでは Groovy を使ってみた。

====

例えば、以下のようなコードで、

class Test {
   def static content = """\
h1. 大見出し

なんとかこんとか
${ new Date().toString() }
"""
      public static void main(args) {
      new RedmineWikiRewriter()
         .login("user001", "password")
         .startEdit("project001", "Http_post_test")
         .saveEdit(content)
   }
}
下の画像のように、wiki ページが更新されるような クラス RedmineWikiRewriter を考えてみる。

  • login() では、与えられたユーザ名とパスワードでログインして、Redmine から返されるクッキーを保持する。
  • startEdit()は、指定のプロジェクトの指定の wiki ページを開く。これはページのバージョンを得るために必要
  • saveEdit()は、wiki コンテンツをマルチパートの HTTP POST で送信する
コードは以下のようなものになる

class RedmineWikiRewriter {
   def static UTF8 = Charset.forName("UTF-8")

   def HTTPBuilder http = new HTTPBuilder('http://localhost:3000/')

   def redmineSession
   def authenticityToken
   def contentVersion 
   
   def project
   def wikiEntry
   
   public RedmineWikiRewriter login(String username, String password) {
      http.get(path : '/login', query : [q:'Groovy'] ) { resp, reader -> 
         authenticityToken = reader.BODY.DIV.DIV
            .findAll { it.id="login-form" }.depthFirst()
            .grep { "authenticity_token"== it.@name.toString() }[0]
            .@value.toString()
      }
      http.request(POST) {
         uri.path = '/login'
         send URLENC, [
            username:username, password:password, 
            authenticity_token:authenticityToken]

         response.success = { resp ->
            redmineSession = resp.getAllHeaders()
               .find {"Set-Cookie"==it.name.toString()}
               .value.split(";")
               .find {it.toString().startsWith("_redmine_session")}
            }
      } 
      this
   }
   RedmineWikiRewriter startEdit(String project, String wikiEntry) {
      this.project = project
      this.wikiEntry = wikiEntry 

      http.request(GET) { resp ->
         uri.path = "/projects/"+project+"/wiki/" + wikiEntry + "/edit"
         headers.'Cookie' = redmineSession
         response.success = { res, reader ->
            contentVersion = reader
               .depthFirst()
               .grep{"content_version"==it.@id.toString() }[0]
               .@value.toString()
         }
      }
      this
   }
   void saveEdit(String content) {
      DefaultHttpClient httpclient = new DefaultHttpClient();
      def uri = "http://localhost:3000/projects/"+ project +"/wiki/" + wikiEntry

      HttpPost httppost = new HttpPost(uri)
      httppost.addHeader('Cookie', redmineSession)
      
      MultipartEntity mpe = new MultipartEntity(
         HttpMultipartMode.BROWSER_COMPATIBLE, 
         "----WebKitFormBoundaryfmexSJQ5daIXdA04", 
         UTF8);
      addPart(mpe, "commit",          "Save");
      addPart(mpe, "project_id",       project);
      addPart(mpe, "action",          "update");
      addPart(mpe, "_method",          "put");
      addPart(mpe, "authenticity_token", authenticityToken)
      addPart(mpe, "id",             wikiEntry)
      addPart(mpe, "content[text]",    content)
      addPart(mpe, "content[version]", contentVersion)
      addPart(mpe, "controller",       "wiki")
      
      httppost.setEntity(mpe);

      httpclient.execute(httppost);
   }
   static void addPart(MultipartEntity entity, String name, String value) {
      entity.addPart(name, new StringBody(value, UTF8));
   } 
}

■ 振り返り

  • saveEdit() でも HttpBuilder を使いたかったが、なぜか思うように動かない。仕方なく、DefaultHttpClient を使用
  • wiki エントリの新規作成でも動くかどうかは未確認
  • たかだかこれくらいのコードでも、やってみると予想外に難しい。Redmine の Rubyコードを読んだり、HTTP の Header を調べたりいろいろ手間がかかった。あと multipart の POST も意外と難しい
  • 仕組みはだいたい分かったので、他の言語でも試してみたい。もともとやりたかったのは、Excel 上の VBA からデータを整形して wiki に載せるというのを自動化すると言う事だった。

2011年11月19日土曜日

Eclipse + Groovy + NekoHTML

Redmine や Trac の wiki などを使ってプロジェクト内で情報共有している HTMLベースのコンテンツを、プログラムで取り扱うやり方を考えていて、Groovy でも使ってみようかと思い立つ。

で、やっぱり Eclipse で使いたいのでプラグインを入れる。Indigo なら update site は ここで、Required とマークされている Groovy Eclipse ってのがあるから、これを選択してインストール。

インストールできたら、お試しプロジェクトを作る。パッケージエクスプローラ上の右クリから New -> Other と進むと、Groovy Project がリストに出てくるから、選択して適当に操作すると、Groovy Project が生成される。

適当に Hello World を書いて、main() メソッド上の右クリから[Run As]を選択すると、サブメニュー、[1 Groovy Console], [2 Groovy Script], [3 Java Application]が表示される。どれを選んでも、同じコードが同じように実行される。

これで、Eclipse で Groovy が使えるようになった。

Groovy 自体、たいして使ったことがないので、練習がてら「wikipedia の適当なページから「他の言語」にリストアップされている言語を、標準出力に書き出す」という簡単なお題をやってみる事にする。

Groovy で HTTP GET や HTML/XML を扱う方法をググりながら、以下のようなコードを書いてみた。

class Ex2 {
   static void main(args){
      new XmlSlurper(
            new org.cyberneko.html.parsers.SAXParser())
         .parse("http://ja.wikipedia.org/wiki/秋刀魚")
         .depthFirst()
         .grep{it.name() == 'DIV' && it.@id == 'p-lang'}[0]
         .DIV.UL.LI.each { println it.text() }
   }
}
Eclipse から実行すると、秋刀魚を解説している10個の言語の名前が書き出される。あまり便利ではなかったが、デバッガも一応使える。

XmlSlurper のコンストラクタに渡しているものは、NekoHtml の SAXParser というやつで、HTML にタグを補完して well-formed な XML として扱えるようにしてくれる。

使えるようにするには、ここから zip か tgz をダウンロードして、中の nekohtml.jar と xercesImpl.jar (または xercesMinimal.jar )を、~/.groovy/lib 下に置けばいい。