Google Apps ScriptでRedmineのチケット情報取得

グレンジ [Advent Calendar 2017](https://qiita.com/advent-calendar/2017/grenge) 17日目の記事を担当しました、k_kimura_01 と申します。
グレンジでサーバサイドエンジニアをしております。

手軽に試せて意外と色々できそうなGoogle Apps Scriptを使ってRedmineの情報を取ってきてみるお話です。

#下準備

RedmineのREST APIを使ってチケット情報を取ってくる為、Redmine側のAPIを有効にしたりAPIキーを取得する必要があります。

APIキーの取得方法やどんなAPIがあるかは以下を参考に。

[RedmineのREST APIについて](http://redmine.jp/glossary/r/rest-api/)

#実践

チケット情報の取得だけであれば、新規のGoogle Apps Scriptを開いて以下のコードを入れることで完了です。

```js
// Basic認証用
var user = 'xxxxx';
var pass = 'xxxxx';
// RedmineAPIキー
var redmineApiKey = 'xxxxxxxxxx';

/**
 * Redmineの該当チケット番号の情報を取得する
 */
function getRedmineIssues() {
  var ticketId = 22222;

  // RedmineAPI用設定
  var url = 'https://redmineUrl.jp/project/issues/' + ticketId + '.json?key=' + redmineApiKey;
  var options = {
    'headers' : {
      'Authorization' : 'Basic ' + Utilities.base64Encode(user + ':' + pass),
    },
    'muteHttpExceptions' : true,
  };

  // Redmineからデータ取得（json形式）
  var response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() != 200) {
    // 存在しないチケット番号だった場合などは何もしない
    Logger.log('getRedmineIssues no ticket');
    return;
  }

  // jsonをオブジェクトに変換
  var redmineIssue = JSON.parse(response.getContentText());

  // チケット情報の一部をためしにログ出力
  Logger.log('fixed_version:' + redmineIssue['issue']['fixed_version']['name']); // 「対象バージョン」
  Logger.log('subject:' + redmineIssue['issue']['subject']); // 「題名」
}
```

##補足1

```js
  // RedmineAPI用設定
  var url = 'https://redmineUrl.jp/project/issues/' + ticketId + '.json?key=' + redmineApiKey;
  var options = {
    'headers' : {
      'Authorization' : 'Basic ' + Utilities.base64Encode(user + ':' + pass),
    },
    'muteHttpExceptions' : true,
  };
```

optionsについてはBasic認証と、HTTPリクエストの結果チェックが不要であれば無くても大丈夫です。
urlの所の「.json」を「.xml」にすればxml形式で取得もできます。

##補足2

```js
  // Redmineからデータ取得（json形式）
  var response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() != 200) {
    // 存在しないチケット番号だった場合などは何もしない
    Logger.log('getRedmineIssues no ticket');
    return;
  }
```

muteHttpExceptionsオプションがtrueでなければレスポンス結果が200以外の時点で例外になる（はず）。
ちなみに取得できるjsonは以下のような感じになります（チケットの内容によって多少変わるかも）。

```json
{
  "issue": {
    "id": 22222,
    "project": {
      "id": 1,
      "name": "projectName"
    },
    "tracker": {
      "id": 1,
      "name": "function"
    },
    "status": {
      "id": 1,
      "name": "new"
    },
    "priority": {
      "id": 1,
      "name": "S"
    },
    "author": {
      "id": 1,
      "name": "k_kimura_01"
    },
    "assigned_to": {
      "id": 1,
      "name": "k_kimura_01"
    },
    "fixed_version": {
      "id": 1,
      "name": "2017/12/17 00:00"
    },
    "parent": {
      "id": 11111
    },
    "subject": "testTitle",
    "description": "testMessage",
    "start_date": "2017-12-17",
    "done_ratio": 0,
    "spent_hours": 0,
    "custom_fields": [
      {
        "id": 1,
        "name": "aaaaa",
        "value": ""
      },
      {
        "id": 2,
        "name": "bbbbb",
        "value": ""
      }
    ],
    "created_on": "2017-12-17T00:00:00Z",
    "updated_on": "2017-12-17T00:00:00Z"
  }
}
```

#おわりに

Google Apps Scriptは、Googleアカウント持っていてちょっとブラウザ立ち上げるだけで気軽にいじれるので興味があればぜひ触って見てくださいませ。

#おまけ（｀・ω・´）

せっかくなので先ほどのRedmineの該当チケット番号の情報を取得するメソッドをベースに、Googleスプレッドシートを使って入力・取得・反映をするサンプル
詳細は省きますが、雰囲気だけ感じ取ってもらえれば（´・ω・｀）

##スクリプト

```js
// Basic認証用
var user = 'xxxxx';
var pass = 'xxxxx';
// RedmineAPIキー
var redmineApiKey = 'xxxxxxxxxx';
// 「チケット番号」のカラム番号
var ticketColumnNo = 1; // A列
// 「タイトル（自動表示）」のカラム番号
var subjectColumnNo = 2; // B列
// 「対象バージョン（自動表示）」のカラム番号
var fixedVersionColumnNo = 3; // C列

/**
 * セル情報を取得する
 */
function getCellValue() {
  var activeSheet = SpreadsheetApp.getActiveSheet(); // アクティブシート取得
  var activeCell = activeSheet.getActiveCell(); // アクティブセル取得
  var activeRange = activeSheet.getActiveRange(); // 選択された範囲を取得する

  // 「チケット番号」カラム列の変更（4行目からを対象）
  if (activeCell.getColumn() == ticketColumnNo && activeCell.getRow() > 3) {
    // Redmineの該当チケット番号の情報をスプレッドシートへ反映
    setRedmineIssues(activeCell, activeRange.getValue());
  }
}

/**
 * Redmineの該当チケット番号の情報をスプレッドシートへ反映する
 *
 * @param object activeCell 選択中のセル情報
 * @param int ticketId チケットID（スプレッドシートから取得）
 */
function setRedmineIssues(activeCell, ticketId) {
  // 数値チェック
  if (!isFinite(ticketId)) {
    Logger.log('setRedmineIssues no number:' + ticketId);
    return;
  }

  // RedmineAPI用設定
  var url = 'https://redmineUrl.jp/project/issues/' + ticketId + '.json?key=' + redmineApiKey;
  var options = {
    'headers' : {
      'Authorization' : 'Basic ' + Utilities.base64Encode(user + ':' + pass),
    },
    'muteHttpExceptions' : true,
  };

  // Redmineからデータ取得
  var response = UrlFetchApp.fetch(url, options);
  if (response.getResponseCode() != 200) {
    // 存在しないチケット番号だった場合などは何もしない
    Logger.log('setRedmineIssues no ticket');
    return;
  }

  // jsonをオブジェクトに変換
  var redmineIssue = JSON.parse(response.getContentText());

  var targetSubjectColumnOffset = subjectColumnNo - ticketColumnNo; // 「タイトル（自動表示）」カラムへ移動する為の値
  var targetFixedVersionColumnOffset = fixedVersionColumnNo - ticketColumnNo; // 「対象バージョン（自動表示）」カラムへ移動する為の値

  // スプレッドシート更新
  activeCell.offset(0,targetSubjectColumnOffset).setValue(redmineIssue['issue']['subject']); // 「タイトル（自動表示）」カラム書き換え
  activeCell.offset(0,targetFixedVersionColumnOffset).setValue(redmineIssue['issue']['fixed_version']['name']); // 「対象バージョン（自動表示）」カラム書き換え
}
```

##トリガーの設定

![トリガー設定.png](https://qiita-image-store.s3.amazonaws.com/0/218917/b4d5d1a9-9af2-647f-0b07-fddb289e6bdb.png)

スプレッドシートで値の変更がされたらセル情報を取得するように設定。
承認が必要ですと出た場合は「許可を確認」で、Googleアカウントログインを。

##スプレッドシート（実行結果）

チケット番号入力前

![シート入力前.png](https://qiita-image-store.s3.amazonaws.com/0/218917/26273ffe-97b7-46a6-dada-97a81f8d22ab.png)

チケット番号入力後

![シート入力後.png](https://qiita-image-store.s3.amazonaws.com/0/218917/1efc6f6d-c26e-a655-2320-6cb555b073af.png)

チケット番号を入力したら、Redmineから取得してきたタイトルと対象バージョンを反映する。