Backlogの課題をGoogleCalendarに連携してみた。

開発の経緯

運用案件で、CMSの構造上「STGに上げた記事は、本番には無条件に全部上がっちゃう」という案件がありました。

クライアントや各制作会社、開発している弊社がみんなでSTG環境を触っている案件ですので、「せめて弊社の分は記事の状態管理をしたいなー...」ということになり、「今STGに上がっているタスクが本番に上がるの(完了日)はいつだろう?」をGoogleCalendarに持っていくことにしました。

用意するもの

  • マシン
  • ブラウザ
  • Backlogプロジェクト
  • Googleアカウント

手順

1. 表示するGoogleCalendarつくる

作成してあるGoogleアカウントのカレンダーから 他のカレンダー の+マークを押して 新しいカレンダーを作成 します。

f:id:MATSUDAE:20200316120932p:plainf:id:MATSUDAE:20200316121013p:plain

連携するのに必要なカレンダーIDをメモしておきます。 f:id:MATSUDAE:20191111123058p:plain

カレンダー側は、一旦おいておきます。

2. BacklogのAPIキーを用意する

BacklogのAPIキーを用意します。

APIキーは、プロジェクト関係なく個人設定からするみたいで、まず迷いました。

本人アイコンを押したら、 APIを選択して、入力したらOK。f:id:MATSUDAE:20191111130337p:plain f:id:MATSUDAE:20191111130442p:plain

こちらも、取得できたら置いておきます。

3. GoogleAppsスクリプトエディタを用意する

Spreadsheetを用意して、スクリプトエディタを開くのが一番手っ取り早いです。

f:id:MATSUDAE:20200316123042p:plain

最近はclaspを使ってVSCodeで開発することも多いです。

その解説まで入れると長くなってしまうので、この解説では直でスクリプトエディタを開いてしまいましょう。

開いたSpreadsheetはlogでも残そうと思っててほったらかしにしちゃいました。あとで実装します。

4. まずはKey関連をセット。

スクリプトエディタにコードを書いていきます。

取得したkey関連をまずはセットします。

// 基本設定
var apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxx',  // 上記2で取得したBacklogのAPIキー
  baseURL = 'https://xxx.backlog.com/api/v2/', //これはBacklog組織ごとに設定。バージョン固定
  taskBaseURL = 'https://xxx.backlog.com/view/';

// STGカレンダー設定
var stgCalendarID = 'xxxxxxxxxxx@group.calendar.google.com', // 上記1でつくったGoogleCalendarのID設定
    stgCalendar = CalendarApp.getCalendarById(stgCalendarID);

5. 必要な情報をset

プロジェクトの情報の中から、カスタム属性を取り出すのに必要な情報を取り出して、必要に応じて置いておきます。

// project情報
var projectName = 'xxxxx', // projectName
    projectURL = baseURL + 'projects/' + projectName + '?apiKey=' + apiKey, // projectデータのAPI
    projectJSON = UrlFetchApp.fetch(projectURL).getContentText(), // projectデータのJSONとってきた
    projectData = JSON.parse(projectJSON), // パースした
    projectID = projectData.id; // パースしたとこからprojectID取得

// custom属性
var projectCustomFieldsURL = baseURL + 'projects/' + projectID + '/customFields' + '?apiKey=' + apiKey,
    customFieldsJSON = UrlFetchApp.fetch(projectCustomFieldsURL).getContentText(),
    customFieldsData = JSON.parse(customFieldsJSON),
    statusFieldName = 'データの状態', // このカスタムフィールドを使用しています
    statusField,
    statusFieldId,
    statusTargetName = 'STG', // この値に合致するものを探してる
    statusTargetId,
    updateFieldName = '新規or更新', // イベントの内容に記入する項目のkey
    updateField;

for (var i in customFieldsData){
  if(customFieldsData[i].name === statusFieldName){ // 'データの状態' の入ってるとこみつける
    statusField = customFieldsData[i];
    statusFieldId = statusField.id;
  }
  if(customFieldsData[i].name === updateFieldName){ // '新規or更新' も同じように。
    updateField = customFieldsData[i];
  }
}

for(var i in statusField.items){
  if(statusField.items[i].name === statusTargetName){ // 'データの状態' の 'STG' を探す
    statusTargetId = statusField.items[i].id;
  }
}

6. 該当箇所のカレンダーをクリアする設定

上書きだけしていくとイベントの量がすごいことになるので、記入する前に前後1ヶ月のデータを削除することにします。

// 前後1ヶ月の日付を作成
function montlyData(){
  var today = new Date(),
      lastMonthDate = new Date(today.setMonth(today.getMonth() - 1)),
      nextMonthDate = new Date(today.setMonth(today.getMonth() + 2)), //TODO: ほんとは分けましょう。
      lastMonth = ('0' + (lastMonthDate.getMonth() + 1)).slice(-2),
      nextMonth = ('0' + (nextMonthDate.getMonth() + 1)).slice(-2),
      lastMonthDay = ('0' + lastMonthDate.getDate()).slice(-2),
      nextMonthDay = ('0' + nextMonthDate.getDate()).slice(-2),
      lastMonthDayText = lastMonthDate.getFullYear() + '-' + lastMonth + '-' + lastMonthDay,
      nextMonthDayText = nextMonthDate.getFullYear() + '-' + nextMonth + '-' + nextMonthDay;
  
  return {
    lastMonthDate: lastMonthDate,
    nextMonthDate: nextMonthDate,
    lastMonthDayText: lastMonthDayText,
    nextMonthDayText: nextMonthDayText
  }
}

// 前後1ヶ月のカレンダーをクリア
function clearCalendarData(taregetCalendar){
  var monthlyDataText = montlyData();
  var events = taregetCalendar.getEvents(monthlyDataText.lastMonthDate, monthlyDataText.nextMonthDate);
  for (var i in events) {
    var event = events[i];
    event.deleteEvent();
  }
}

7. 前後1ヶ月の課題を取り出す

プロジェクトの課題のうち、前後1ヶ月のものを取り出します。

// 課題
// (期限日が前後1ヶ月のもの)
function getIssues(){
  var projectIssuesURL = baseURL + 'issues?apiKey=' + apiKey + '&projectId[]=' + projectID + '&dueDateSince=' + montlyData().lastMonthDayText + '&dueDateUntil=' + montlyData().nextMonthDayText,
    projectIssuesJSON = UrlFetchApp.fetch(projectIssuesURL).getContentText(),
    projectIssuesData = JSON.parse(projectIssuesJSON);
  
  return projectIssuesData;
}

8. 課題をカレンダーにセット

取り出した課題渡してカレンダーのイベントとして登録する準備。

// 課題をカレンダーにセット
function setIssueToCalendar(issue, targetCalendar){
  var issueKey = issue.issueKey,
      summary = issue.summary,
      dueDate = new Date(issue.dueDate),
      url = taskBaseURL + issueKey,
      status = issue.status.name,
      customFields = issue.customFields,
      dataStatus,
      dataUpdate,
      descriptionText,
      options;
  
  for (var i in customFields) {
    if(customFields[i].name === statusFieldName && customFields[i].value !== null){
      dataStatus = customFields[i].value.name;
    }
    if(customFields[i].name === updateFieldName && customFields[i].value !== null){
      dataUpdate = customFields[i].value.name;
    }
  }
  descriptionText = issueKey +' '+ summary + '\n';
  descriptionText += 'タスクの状態: ' + status + '\n';
  descriptionText += 'url: ' + url + '\n';
  descriptionText += statusFieldName + ': ' + dataStatus + '\n';
  descriptionText += updateFieldName + ': ' + dataUpdate + '\n';
  
  options = {
    description: descriptionText
  }
  targetCalendar.createAllDayEvent(issueKey +' '+ summary , dueDate, options);
}

9. カレンダーに書き込む

全部準備ができたので、課題1件ずつをカレンダーに登録したら完了です。

// STG状態calendar書き込み
function writeStgCalendar(){
  clearCalendarData(stgCalendar);
  var stgIssuesData = getStgIssues();
  for (var i in stgIssuesData) {
    var issue = stgIssuesData[i];
    setIssueToCalendar(issue, stgCalendar);
  }
}

感想

  • こうしてみると全然簡単ですが、まず BacklogのAPIキーって、誰が発行権限もってるんだ? ってとこから始まりました。 -> Backlog入ってる人はだれでも発行できます!
  • ひとまず無制限に課題を引っ張り出してきたら、多分私の入っていないプロジェクトのデータまでずるっと引き出せてしまったので「あわわわ...」ってなりました。
  • Backlogで更新あったらカレンダーに...っていうのは更新あったのを取ることができなさそうなのでGAS側のトリガーで時間指定で設定しました。
  • この程度のことなららくちんだなー。Backlogのデータ、GASでいじるのはわりと楽な気がします。

「こういうのあったらいいなー」からAPIを調べて実装、までで1日ちょいくらいでできました。

BacklogのAPIは課題に関してはわりと使いやすいイメージです。「あともうちょっとコレがあれば...!」という機能を思いついたら、さくっと作っちゃうのがオススメですよ!