Trello REST APIを使用して複数タスクを一括登録する

f:id:hamo2020:20200509102737j:plain

マネックス証券 システム開発部のHです。

はじめに

私は日常ToDo管理にTrelloを使用しているのですが、複数のタスクを一括登録できると便利かと思ったので、ツールを作成してみました。

Trelloのボード

次のようなボードがあり「MyList」というリストがあります。ここにカード(タスク)を追加していきます。 f:id:hamo2020:20200508155814p:plain

やりたいこと

下表のような複数のタスク(CSVファイル)をTrelloのREST APIを使用して一括登録します。 Linuxやcygwinなどでシェルスクリプトを作成して、内部でcURLを使ってリクエスト送信(API実行)をすると簡単にできるかと思いますが、今回はJavaを使用します。 f:id:hamo2020:20200509103420p:plain

Trello REST APIについて

Trello REST APIについては、下記にリファレンスがあります。 developer.atlassian.com

ただAPIを利用するには、API KeyとAPI Tokenを取得する必要があります。 解説ページがいろいろあるので、そちらを参考にして取得しました。

blog.serverworks.co.jp qiita.com developer.atlassian.com

今回はAPIの中から「Create a new Card」を使用します。 これはその名の通り、1つのカードを作成します。 クエリパラメータとして渡すものは下記の通りです。

クエリパラメータ 説明
API Key APIキー
API Token APIトークン
name カードのタイトル
desc 説明
due 期限
idList リストのID

「Create a new Card」を使用するにあたり、リスト(MyList)のIDも必要となるのですが、上記のリンク先などにも取得方法の記載があるので、今回は割愛します。

処理の流れ

処理については、このシーケンス図のようにCSVからタスクを読み込んで、タスク数分Trelloへ「Create a new Card」のリクエスト送信を行い、レスポンスを表示するといったごくシンプルな作りにします。 f:id:hamo2020:20200508150844p:plain

1. CSVファイルから読み込む

CSVから読み込んだタスクをリストに格納します。 CSVからタスクを読み込むにあたり、読み込んだデータを保持するDTO(Data Transfer Object)的なオブジェクトをCardという名前で定義します。 「Create a new Card」のクエリパラメータ(name、desc、due)とCSVの項目(タイトル、説明、期限)をマッピングさせます。

クエリパラメータ 説明 CSVの項目
API Key APIキー なし(固定)
API Token APIトークン なし(固定)
name カードのタイトル タイトル
desc 説明 やること
due 期限 期限
idList リストのID なし(固定)

f:id:hamo2020:20200509130345p:plain

こんな感じでCSVの内容をリストに格納します。

private List<Card> getCardsFromCsv(String filename) throws IOException {
    return  Files.lines(Path.of(filename), Charset.forName("MS932"))
            .filter(s -> !s.startsWith("タイトル"))
            .collect(ArrayList<Card>::new,
                    (t, s) -> t.add(new Card(s)),
                    (t, u) -> t.addAll(u));

}

2. リクエストの作成

Trelloへリクエストを送信するためのHTTPクライアントは、OkHttpを使用します。 square.github.io リクエストは次のように作成します。

MediaType MIMEType= MediaType.parse("text/plain; charset=sjis");
RequestBody requestBody = RequestBody.create("", MIMEType);
Request req = new Request.Builder()
        .url(createUrl(card)) 
        .post(requestBody) // メソッドはPOSTを指定
        .build();

createUrl()ではクエリ文パラメータを付与したURL(HttpUrl)のオブジェクトを作成します。

private HttpUrl createUrl(Card card) {
    HttpUrl url = new HttpUrl.Builder()
            .scheme(conf.getScheme())
            .host(conf.getHost())
            .addPathSegments(conf.getUrlCreateANewCard())
            .addQueryParameter(API_KEY, conf.getKey())
            .addQueryParameter(API_TOKEN, conf.getToken())
            .addQueryParameter("name", card.getName())
            .addQueryParameter("desc", card.getDesc())
            .addQueryParameter("due", card.getDue())
            .addQueryParameter("idList", conf.getListId())
            .build();

    return url;
}

3. Trelloへリクエスト送信(Create a new Card)、4. レスポンス

Trelloへリクエスト送信は次のようにOkHttpClientのオブジェクトを作成し、newCall().execute()のメソッドチェーンで行います。

OkHttpClient client = new OkHttpClient()
        .newBuilder()
        .proxy(proxy)
        .build();
try (Response res = client
        .newCall(req)
        .execute()) {
    System.out.println(res.body().string());
} catch (IOException e) {
    e.printStackTrace();
}

5. レスポンスの表示

Trelloから受信したレスポンスはこのようになります。 name、desc、dueにリクエストで指定した値が設定されています。
※dueについては、UTCでの表記のため、日本時間から9時間マイナスとなっています。

{
  "id": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "checkItemStates": [],
  "closed": false,
  "dateLastActivity": "2020-05-08T09:07:58.499Z",
  "desc": "交通費について申請する",
  "descData": {
    "emoji": {}
  },
  "dueReminder": null,
  "idBoard": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "idList": "XXXXXXXXXXXXXXXXXXXXXXXX",
  "idMembersVoted": [],
  "idShort": 60,
  "idAttachmentCover": null,
  "idLabels": [],
  "manualCoverAttachment": false,
  "name": "経費精算",
  "pos": 163839,
  "shortLink": "XXXXXXXX",
  "isTemplate": false,
  "dueComplete": false,
  "due": "2020-05-15T08:00:00.000Z",
  "email": null,
  "labels": [],
  "shortUrl": "https://trello.com/c/XXXXXXXX",
  "url": "https://trello.com/c/XXXXXXXX/60-%E7%B5%8C%E8%B2%BB%E7%B2%BE%E7%AE%97",
  "cover": {
    "idAttachment": null,
    "color": null,
    "idUploadedBackground": null,
    "size": "normal",
    "brightness": "light"
  },
  "idMembers": [],
  "attachments": [],
  "badges": {
    "attachmentsByType": {
      "trello": {
        "board": 0,
        "card": 0
      }
    },
    "location": false,
    "votes": 0,
    "viewingMemberVoted": false,
    "subscribed": false,
    "fogbugz": "",
    "checkItems": 0,
    "checkItemsChecked": 0,
    "checkItemsEarliestDue": null,
    "comments": 0,
    "attachments": 0,
    "description": true,
    "due": "2020-05-15T08:00:00.000Z",
    "dueComplete": false
  },
  "subscribed": false,
  "idChecklists": [],
  "stickers": [],
  "limits": {}
}

複数タスクの一括登録

上記の2~5は1つのタスクについての処理だったので、これを複数タスクの一括登録にすると、次のようになります。

private void sendRequests(List<Card> cards) throws IOException {
    SocketAddress addr = new InetSocketAddress(
            conf.getProxyHost(), conf.getProxyPort());
    Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
    OkHttpClient client = new OkHttpClient()
            .newBuilder()
            .proxy(proxy)
            .build();
    cards.stream()
        .forEach(c -> sendRequest(c, client));
}

private void sendRequest(Card card, OkHttpClient client) {
    MediaType MIMEType= MediaType.parse("text/plain; charset=sjis");
    RequestBody requestBody = RequestBody.create("", MIMEType);
    Request req = new Request.Builder()
            .url(createUrl(card))
            .post(requestBody)
            .build();
    try (Response res = client
            .newCall(req)
            .execute()) {
        System.out.println(res.body().string());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Trelloの画面上ではMyListに4つのカードが追加されています。 f:id:hamo2020:20200508183618p:plain

説明、期限についてもリクエストで指定したものが設定されています。 f:id:hamo2020:20200509143919p:plain

おわりに

Trello REST APIやOkHttpについては、関連する記事がいろいろあるので、特に大きな壁にぶつかることもなく、複数タスクの一括登録を実現することができました。