Backlog API でカテゴリーが未設定になっている全ての課題に対して特定のカテゴリーを一括設定する

はじめに

テントの中から失礼します、CX 事業本部のてんとタカハシです!

業務でタスク管理には Backlog を使用しています。短期間のプロジェクトでは、タスクを細かくカテゴリー分けせず進めていたりするのですが、時間が経ってやっぱりカテゴリー分けとかしたいよねって時があります、というか、ありました。

既に多くのタスクが作成されている状態で、1つずつ画面ぽちぽちしてカテゴリーを設定するのはちょっと面倒だよねって気持ちから、それを解決するスクリプトを書いてみました。これのおかげで80個近くあったタスクを1~2分で設定変更できました、わーい。

環境

プロジェクトで TypeScript を使用していたので、そのままそれで書きました。

% sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H2

% yarn --version
1.22.10

% node --version
v14.7.0

% npx tsc -v
Version 4.3.2

% npx ts-node -v
v10.0.0

必要なライブラリをインストールする

ヌーラボさんが用意している backlog-js を使用して、より簡単に API を叩けるようにします。内部で Fetch や FormData が使用されているため、Node.js でも実行できるように代わりとなるライブラリもインストールします。

% yarn add backlog-js
% yarn add isomorphic-fetch
% yarn add isomorphic-form-data

必要な各種 ID を確認する

プロジェクト ID

「プロジェクト設定」ページの URL からプロジェクト ID を確認することができます。

https://<HOST_NAME>/EditProject.action?project.id=<PROJECT_ID>

カテゴリー ID

プロジェクト設定ページの中にある「カテゴリー」ページに移動するとカテゴリーの一覧が表示されます。設定したいカテゴリーの編集ページに移動すると URL からカテゴリー ID を確認することができます。

https://<HOST_NAME>/EditComponent.action?component.id=<CATEGORY_ID>&component.projectId=<PROJECT_ID>

ソースコード

getIssuesでカテゴリーが未設定になっている課題を取得してからpatchIssueでカテゴリー ID を設定しています。

getIssues する際の引数 categoryId に-1を指定することで、カテゴリーが未設定の課題のみを取得することができます。また、引数 keyword の指定が必須になっているのですが、空文字を指定することで、フィルターせずに全取得できるようです。念のため、patchIssue した後は、1秒間の wait を入れています。

import 'isomorphic-fetch';
import 'isomorphic-form-data';
import * as backlogjs from 'backlog-js';

// この辺の値は各々の環境に合わせて変更してください
const host = '<HOST_NAME>'; // hoge.example.com
const apiKey = '<API_KEY>';
const projectId = <PROJECT_ID>; // 数値
const categoryId = <CATEGORY_ID>; // 数値

const backlog = new backlogjs.Backlog({ host, apiKey });

const sleep = (ms: number): Promise<void> =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, ms);
  });

(async function () {
  let issueList = [];
  do {
    // カテゴリーが未設定の課題を取得する
    issueList = await backlog.getIssues({
      projectId: [projectId],
      categoryId: [-1], // -1 は未設定を表す
      keyword: '', // 指定が必須にはなるが、空文字にするとフィルターしないみたい
    });

    for (const issue of issueList) {
      // 特定のカテゴリーに変更する
      await backlog.patchIssue(issue.issueKey, {
        categoryId: [categoryId],
      });

      // 瞬時に API を叩きまくらないように wait を挟んでおく
      await sleep(1000);
    }
  } while (issueList.length !== 0);

  console.log('Done!');
})();

おわりに

Backlog API を駆使すれば色々な面倒ごとを回避できそうですね。今後も何かネタがあれば記事にしようと思います。

今回は以上になります。最後まで読んで頂きありがとうございました!