GMOアドマーケティング SSP開発チームのKA.Mです。前回紹介したJIRA x GitHub自動連携の記事は見て頂けましたでしょうか。まだ、ご覧でない方は先にそちらを見て頂けると幸いです。

前回の記事ではJIRA x GitHubの自動連携を実現し、開発中のステータス移動がほぼ発生しない事を実現しました。私的にはこの時点でだいぶ満足で特に不満もありませんでした。
そう当時は。。。
月日が流れ私も入社1年と半年強です。気づいた頃にはディレクタとのやり取りもずいぶん増えてきました。ディレクタから開発への依頼はTrelloを用いています。
Trelloは言わずとしれたアトラシアン株式会社様の製品となります。TrelloはJIRAよりもカジュアルにタスクを作成し、気軽に開発チームに依頼できる便利なツールです。ディレクタとのやり取りが増え気づいた事があります。
JIRAへの同期が何回もクリックしないといけなく、手間であると。
そこで今回はテックブログネタとしてTrelloのWebhookを活用して、簡単なJIRA連携についてご紹介したいと思います。
はじめに
TrelloのWebhookの紹介ページを見てみましょう。
紹介ページをもとに実際に登録していきます。
まずはじめにWebhookの登録処理をPHPで実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public function postTokensTokenWebhooks(): array { $url = sprintf('https://api.trello.com/1/tokens/%s/webhooks', $this->token) . '?' . http_build_query([ 'key' => $this->key, 'idModel' => $this->idBoard, 'callbackURL' => $this->callbackUrl, ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, CURLOPT_POST => true, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } |
こんな感じの実装になると思います。
ここで必要となる情報は以下の通りです。
- key
- token
- idModel
- CallbackURL
key, token, secretの発行
Trelloにログインした状態で下記ページにアクセスします。ページから必要となるkey, token, secret情報を取得します。secretは署名判定処理で使用します。
idModelの確認
TrelloのWebhookはidMoelベースでWebhookを設定する事が出来ます。
今回はTrelloのボード自体にWebhookを設定してみます。でははじめに先程発行したtoken情報からtokenに関わる情報を取得してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public function getTokensToken(): array { $url = sprintf('https://api.trello.com/1/tokens/%s?token=%s&key=%s', $this->token, $this->token, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } //Result //[ // 'idMember' => 'xxxxxxxx', //] |
実行する事でtokenを発行したメンバー情報が確認できるでしょう。続いて取得した idMember
情報から所属しているボード情報を取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public function getMembersId(string $idMember): array { $url = sprintf('https://api.trello.com/1/members/%s?token=%s&key=%s',$idMember, $this->token, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } //Result //[ // 'idBoards' => [ // 'aaaaaaaa', // 'bbbbbbbb', // 'cccccccc', // ], //] |
コレで設定する idBoard
情報は取得できましたが、どの idBoard
を設定するのか現状わかりません。そこで各 idBoard
の詳細を見ていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public function getBoardsId(string $idBoard): array { $url = sprintf('https://api.trello.com/1/boards/%s?token=%s&key=%s', $idBoard, $this->token, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } //Result //[ // 'name' => 'GMOSSPのTrello', // 'id' => 'cccccccc', //] |
確認した所 idBoard
は cccccccc
が本来使用したかったIDである事がわかります。CallbackURLはイベント発火時に呼ばれるURLとなるため、事前に用意しておきましょう。これでWebhook登録に必要な情報が全て揃いましたので、とりあえず試しに登録してみましょう、無事登録出来ると思います。
Webhookの削除
試しに登録した後は削除する必要があると思います。そこで現在登録されているWebhookの情報を確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public function getTokensTokenWebhooks(): array { $url = sprintf('https://api.trello.com/1/tokens/%s/webhooks?key=%s', $this->token, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } //Result //[ // [ // 'id' => 'yyyyyyyy', // 'callbackURL' => '指定したCallbackURL', // ] //] |
実行結果からidWebhookの yyyyyyyy
を今回は削除したいとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public function deleteTokensTokenWebhooksIdwebhook(string $idWebhook): array { $url = sprintf('https://api.trello.com/1/tokens/%s/webhooks/%s?key=%s', $this->token, $idWebhook, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, CURLOPT_CUSTOMREQUEST => 'DELETE', ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } |
これで削除出来ました。削除できたかを確認する場合は先に実行した設定されているWebhook一覧を再度呼ぶ事で現在の設定状況が確認できます。これでひとまずWebhookの登録は完了しました。
署名チェック
Webhookの登録が完了したタイミングから実際にイベントが実行されCallbackURLが呼ばれるようになります。そこでヘッダの情報を検証してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
public function isValidSignature(): bool { $header = getallheaders(); if (!isset($header['x-trello-webhook'])) { return false; } $postJson = file_get_contents('php://input'); $content = $postJson . $this->callbackUrl; return hash_equals(base64_encode(hash_hmac('sha1', $content, $this->secret, true)), $header['x-trello-webhook']); } |
問題なく機能すると true
になるかと思います。
Trelloカード情報取得
Trelloのリクエスト情報は実際に操作してみて確認するのが早いでしょう。今回は『Trelloでリスト間移動イベント』の情報を見てみます。具体的な利用ケースはディレクタの企画に対して工数見積を行い、開発するとなった時に『未着手』リストにカードを移動させたときが今回のケースとなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//[ // 'action' => [ // 'data' => [ // 'listAfter' => [ // 'name' => '未着手', // 'id' => 'dddddddddddddddddddddddd', // ], // 'listBefore' => [ // 'name' => '提案', // 'id' => 'eeeeeeeeeeeeeeeeeeeeeeee', // ], // 'card' => [ // 'id' => 'ffffffffffffffffffffffff', // ] // ] // ] //] |
リスト間移動をおこなった場合は上のような感じのリクエストがCallbackURLに届きます。この情報を元にカード情報を取得します。念の為、すでにJIRA連携していないかも確認した方が良いでしょう。すでにJIRAに連携済みであれば連携しないにこした事はありません。API仕様を確認しながら挙動確認した所、 attachments
の情報を元に判断するのが良いとわかりました。ここで取得した情報を元にJIRA連携を実施します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public function getCardsId(string $idCard): array { $url = sprintf('https://api.trello.com/1/cards/%s?token=%s&key=%s&attachments=true', $idCard, $this->token, $this->key); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ 'Accept: application/json', ], CURLOPT_URL => $url, ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 200) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } //Result //[ // 'id' => '5c519123ab131169be28da9c', // 'desc' => '概要', // 'attachments' => [ // ], // 'name' => 'タイトル', //] |
JIRAタスク作成
前回の続きという事ですでに認証情報は取得している前提となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public function postIssue(array $cardArray): array { $url = 'https://xxxxxx.atlassian.net/rest/api/2/issue'; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => false, CURLOPT_HTTPHEADER => [ "Cookie: {$this->_auth['session']['name']}={$this->_auth['session']['value']}", 'Accept: application/json', 'Content-type: application/json', ], CURLOPT_URL => $url, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode([ 'fields' => [ 'project' => [ 'key' => 'JIRAのKey情報', ], 'summary' => $cardArray['name'], 'issuetype' => [ 'name' => 'Task', ], 'description' => $cardArray['desc'], 'TrelloのidCard登録フィールド' => $cardArray['id'], ], ]), ]); $body = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if ($info['http_code'] !== 201) { throw new RuntimeException($body, $info['http_code']); } return json_decode($body, true); } |
これでJIRAにタスクが作成されました。この他にもGMOSSPでは自動で変わるJIRAのステータスに合わせて、Trelloのリストを移動させ、 未着手
> 着手中
> リリース
と自動でリスト移動を行うようにしました。まだ、コメントや添付ファイル連携など検討要素は多々ありますが、まずは自動化を実現できたのではと考えています。
まとめ
今回はTrelloのWebhookを利用する事でJIRA、GitHubとの連携をシームレスに実現しました。リアルタイムに反映される事でより快適な開発環境が実現できたのではないかと思います。