• このエントリーをはてなブックマークに追加


ss-1386306622

はじめに

1か月ほど前から,ようやく Trello を使い始めてみている issm です.

その単純さ故に使えるようになるためのルールが非常に少なく(と私は感じています),その操作方法も直感的なことや,iOS / Android アプリの存在によって,あまり場所に影響されずに(最近はほとんど外出していませんが)情報にアクセスできることなどから,「頭に入れずに外部の特定の場所にためておく」ためのツールとして,個人的に大ヒットしつつあります.(似たような目的で以前に こんなもの を作ろうとしていましたが,もう飽きた)

とりあえずここ長らく続いているひとつの「プロジェクト」的お仕事を,ひとつの「Board」に割り当て,無数のタスクを「Card」として登録し,それらをごにょごにょ試行錯誤してみています.その中には,BitBucket で管理しているリポジトリの issue に相当するものもありますが,これまで,BitBucket で issue を登録 → Trello で Card を登録...みたいなことを手動的に行うことを続けてきており,その辺りの作業感への嫌気が 8 合目くらいまでに達してきました.

Trello 側には,その各種のリソースを操作するための API が充実していますが,BitBucket 側では,「Hooks」の一覧を見る限り,今のところそれらとは連携していないっぽくて残念.ただその中で気になったのが「POST」,「リポジトリに『変化』があった場合に,指定の URL へリクエストが行くよ」というもの.テキトーな場所でこのリクエストを受けて情報を加工し,ユーザ issm として Trello API へ投げればいいじゃない.ただ,Trello の API リファレンス を見る限り,少々ややこしそう...

そんな背景の下,特定の情報を加工して Trello API へ投げられること,API のややこしさを表向きには少しでも解消したいこと,あたりをコンセプトとして,昨日夕方から手を動かして,Trexyll という API プロキシ的なものをこさえてみました:

以下,その紹介とか簡単な使い方とかについて書いていきます.

主な特徴

今のところ,次の 3機能をサポートしています.「とりあえずこれだけできればいっか」という程度の実装です:

  • 公式 API へパススルー
  • カスタマイズ可能な API
  • キーのチェック

それぞれの詳細を後述します.

Getting Started

インストールする

% git clone https://github.com/issm/Trexyll.git
% cd Trexyll
% carton

Application Key と Token を取得,設定する

とりあえず詳細は省略,次あたりを参照してください:

...で,取得した両者を基に,次のような TOML なファイルを準備します.ファイル名は任意ですが,ここでは /path/to/authz.toml としておきます.

key   = "substitutewithyourapplicationkey"
token = "substitutethispartwiththeauthorizationtokenthatyougotfromtheuser"

コンフィグファイル(config/development.pl とか)を設定します:

+{
    'trello.api' => {
        authz_file => '/path/to/authz.toml', # TOML
    },
};

サーバを起動する

% carton exec -- perl script/server

http://localhost:5525/hello へ GET リクエストして,次のようなレスポンスがあれば OK です:

{"hello":"trexyll"}

公式 API へパススルー

http://localhost:5525/-/ 以下へのリクエストは,https://trello.com/docs/api/index.html にリストされている API へそのままパススルーされます.

例えば,http://localhost:5525/-/members/me への GET リクエストは,公式の API GET /1/members/[idMember or username]
に対応する,という感じです.

% curl http://localhost:5525/-/members/me | jq .
{
  "original_headers": [
    "Cache-Control: max-age=0, must-revalidate, no-cache, no-store",
    "Connection: close",
    "Date: Fri, 06 Dec 2013 04:38:44 GMT",
    ...
  ],
  "data": {
    "bio": "",
    "idBoards": [
       ...
    ],
    "idBoardsPinned": [
       ...
    ],
    "products": [],
    "username": "issm",
    "idBoardsInvited": [],
    "loginTypes": null,
    "idOrganizations": [],
    "uploadedAvatarHash": null,
    "status": "idle",
    "newEmail": null,
    "bioData": {
      "emoji": {}
    },
    ...
  },
  "original_status_code": "200"
}

レスポンス JSON は,次の 3つの要素(?)で構成されています:

  • data: 公式 API の実際のレスポンスデータ
  • original_status_code: 公式 API の実際のレスポンスのステータスコード
  • original_headers: 公式 API の実際のレスポンスのヘッダ

カスタマイズ可能な API

http://localhost:5525/c/ 以下は,ユーザが独自で定義した処理を挟むことのできる領域です.

Trexyll::Custom->register(...) を呼び出すためのスクリプトを記述し,custom/*.pl のように配置することで,独自定義した API を利用できます.定義する内容は,次のような感じです:

  • 受け付けるリクエストメソッド
  • API のパス(/c/{ここに当てはまります}
  • クエリパラメータ加工処理
  • 対象公式 API の名前

定義の例:

# 現状,ファイルの拡張子は .pl でなければならない!
use strict;
use warnings;
use utf8;
use Trexyll::Custom;
 
Trexyll::Custom->register(
    # 許可するリクエストメソッド
    #   Str|ArrayRef
    method => [qw/POST/],
 
    # /c/ 以下のパス
    #   Str|ArrayRef[Str]
    path => 'add_card',
 
    # 公式 API へ渡すクエリパラメータ
    #   CodeRef -> HashRef
    query => sub {
        my ($self, $req) = @_;
        +{
            pos  => 'top',
            name => 'Trexyll のカスタム API からテスト!',
            desc => 'カードの概要カードの概要',
            due  => '2013-12-14',
        };
    },
 
    # 対象となる公式 API の名前(パス?)
    #   Str|(CodeRef -> Str)
    target => 'lists/52a140....../cards',
);

こんな感じのファイルを配置することで,http://localhost:5525/c/add_card という URL で「特定のリストに『Trexyll のカスタム API からテスト!』という名前の入ったカードを追加する」という API を利用できるようになるわけです.

httpie でリクエストした場合の例:

% http post http://localhost:5525/c/add_card | jq .
{
  "original_status_code": "200",
  "data": {
    "pos": 16384,
    "url": "https://trello.com/c/...",
    "desc": "カードの概要カードの概要",
    "name": "Trexyll のカスタム API からテスト!",
    "descData": {
      "emoji": {}
    },
    "id": "52a15c......",
    "closed": false,
    "idAttachmentCover": null,
    "idChecklists": [],
    "due": "2013-12-14T00:00:00.000Z",
    ...
  },
  "original_headers": [
    "Cache-Control: max-age=0, must-revalidate, no-cache, no-store",
    "Connection: close",
    "Date: Fri, 06 Dec 2013 05:10:00 GMT",
    "Vary: Accept-Encoding",
    "Content-Length: 764",
    "Content-Type: application/json",
    ...
  ]
}

こんな感じでカードが追加されました!:

ss-1386306622

ちなみに,この URL に対して GET リクエストを行った場合,次のようになります:

% http get http://localhost:5525/c/add_card | jq .
{
  "message": "not found"
}

キーのチェック

http://localhost:5525/v/check で,設定している Application Key と Token が正しいものかどうかを確認できます.まぁ特定の API を叩いて 200 が返るか否かで判別しているわけですが...

今のところ,read が云々 write が云々,といったところまでは対応していません.

設定が正しいときの例:

% http get http://localhost:5525/v/check | jq .
{
  "result": "ok",
  "original_response": {
    "status_code": "200",
    "headers": [
      "Cache-Control: max-age=0, must-revalidate, no-cache, no-store",
      "Connection: close",
      "Date: Fri, 06 Dec 2013 05:17:20 GMT",
      ...
    ],
    "status_line": "200 OK",
    "content": "{\"id\":\"52a02f......\",...}"
  }
}

設定が正しくないときの例:

% http get http://localhost:5525/v/check | jq .
{
  "result": "fail",
  "original_response": {
    "status_line": "401 Unauthorized",
    "status_code": "401",
    "headers": [
      "Cache-Control: max-age=0, must-revalidate, no-cache, no-store",
      "Connection: close",
      "Date: Fri, 06 Dec 2013 05:17:40 GMT",
      ...
    ],
    "content": "invalid token\n"
  }
}

おわりに

以上,Trello API のプロキシ的なもの「Trexyll」を作ってみた,というお話でした.

さて,いったんお仕事に戻りますが,金土の境目あたりで BitBucket/issue -(Trexyll)-> Trello/Card の連携を実現してみたいところ.というか,これが本来の目的じゃないかw

なお,今回の開発ごっこでは,初めて使用した次の CPAN モジュールのおかげで,作り上げるのが非常に捗りました.感謝!:

そんなわけで,Trello をオススメしてみるテスト