Jira Cloud REST APIとGASを使ってJQLで指定した条件にマッチする課題を抽出する(Basic認証版)

#  背景と目的

Google スプレッドシートには「[Jira Cloud for Sheets](https://workspace.google.com/marketplace/app/jira_cloud_for_sheets/1065669263016?hl=ja)」というアドオンが有り、Jira Cloud から指定した条件にマッチする課題を簡単に抽出することができます。

先日、Atlassian から[このアドオンの提供が終了する](https://community.atlassian.com/t5/Marketplace-Apps-Integrations/Updated-Deprecating-the-Jira-Cloud-for-Google-Sheets-app/ba-p/2106224)という発表が有りましたが、コミュニティからの猛反対が有り、撤回されました。

https://community.atlassian.com/t5/Marketplace-Apps-Integrations/Updated-Deprecating-the-Jira-Cloud-for-Google-Sheets-app/ba-p/2106224

ただし、個人的には

- 今後何かの理由でアドオンの提供・アップデートが停止される可能性は 0 ではない
- アドオンではカスタマイズできない設定などをカスタマイズしたい

という思いがあったので、「それなら REST API と GAS を使って同じような機能を実装しよう」と思い立ち、JQL で指定した条件にマッチする課題を抽出する GAS を実際に実装しました。

今回はその実装方法を紹介しようと思います。

# JQL とは

最初に **JQL** について簡単に説明します。
JQL は **J**ira **Q**uery **L**anguage の略で、Jira で特定の課題を検索するためのクエリ言語です。
以下に Atlassian の記事を貼っておきますので、JQL を使ったことが無い方は是非ご参照ください。
- [はじめてのJQL 第1回：Jiraでの検索の概要とJQLの基礎 | Atlassian Community](https://www.atlassian.com/ja/blog/jql-for-beginners-1)
- [Jira クエリ言語 (JQL) で高度な検索を使用する | Atlassian Support](https://support.atlassian.com/ja/jira-software-cloud/docs/use-advanced-search-with-jira-query-language-jql/)

# Jira Cloud REST API を使用する準備

## API トークンの取得
Atlassian 製品の REST API を使用するには API トークン が必要になります。
API トークンを取得するには、自分の Atlassian アカウント管理ページの [セキュリティ] → [API トークン] → [[API トークンの作成と管理](https://id.atlassian.com/manage-profile/security/api-tokens)] にアクセスし、作成します。

## Basic 認証方法
Jira Cloud REST API を実行する場合、取得した API トークン `your-api-token` と自分の Atlassian アカウント `your-account@example.com` を使用して Basic 認証します。
詳しい仕様については以下の記事をご確認ください。

- [Basic auth for REST APIs | Atlassian Developer](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/)

## リクエストを送信するためのサンプルコード
以下にリクエストを送信するためのサンプルコードを掲載します。

```js:GAS
function sendRequest(method, resource, params) {
  let url = "https://your-domain.atlassian.net" + resource;
  const account = "your-account@example.com";
  const apiToken = "your-api-token"; 
  let options = {
    "method" : method,
    "headers" : {
      "Content-Type" : "application/json",
      "Authorization" : "Basic " + Utilities.base64Encode(account + ":" + apiToken),
    },
    "muteHttpExceptions" : true
  }

  if (params!=""){
    options["payload"] = JSON.stringify(params);
  }

  const response = UrlFetchApp.fetch(url, options);
  const responseCode = response.getResponseCode();
  const responseBody = response.getContentText();

  if ([200, 201].indexOf(responseCode)<0){
    throw new Error(responseCode + " : " + responseBody);
  }
  
  return JSON.parse(responseBody);  
}
```

- `https://your-domain.atlassian.net` の `your-domain` はご自身の環境に合わせて設定してください。
- `method` には　`GET` や `POST` を指定します。
- `resource` には `/rest/api/3/<resource-name>` など、使用したい REST API のリソースを指定します。
- `params` にはオプションのパラメーターなどを指定します。

# 課題フィールドの一覧を取得する
REST API で Jira の課題を取得すると、カスタムフィールドは `customfield_10000` のように表示されます。これだと何の課題フィールドなのかわからないので、課題フィールドの一覧を取得し、課題フィールドの ID と名前の対応表を準備しておきます。

課題フィールドの一覧を取得するには `resource` に `/rest/api/3/field` を指定します。
詳しい仕様は以下の記事をご確認ください。

- [Get fields｜Atlassian Developer](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-fields/#api-rest-api-3-field-get)

## サンプルコード
以下に課題フィールドを取得するサンプルコードを掲載します。

```js:GAS
function getIssueFields() {
  try{
    const resource = "/rest/api/3/field";
    const fields = sendRequest("GET", resource, "");
    return fields;
  } catch(e) {
    return {"error": e.message};
  }
}
```

取得した課題フィールドの情報 `fields` を加工し、お好みの形式で Google スプレッドシートに書き出して課題フィールドの ID と名前の対応表を作成してください。

# JQL で指定した課題を取得する

JQL で指定した課題を取得するには `resource` に `/rest/api/3/search` を指定します。
詳しい仕様は以下の記事をご確認ください。

- [Search for issues using JQL (GET) | Atlassian Developer](https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-get)

## サンプルコード
以下に JQL で指定した条件にマッチする課題を取得するサンプルコードを掲載します。

- 1回のリクエスト送信で取得できる課題は、デフォルトでは50件です。今回は1回に100件取得できるように `maxResults` に `100` を設定しています。また、`maxResults` に設定できる最大値は `100` です。
- 条件にマッチする課題が100件以上ある場合、全ての情報を取得するまでリクエスト送信を行います。
- `jql` に条件を設定しないでリクエストを送信した場合、ご自身の Jira インスタンスに存在するアクセス可能なすべての課題が条件にマッチする課題になります。ただし、上記の仕様を組み込んでいるため、意図せず大量の課題を取得してしまわないように `jql` が未設定の場合はエラーになるようにしています。


```js:GAS
function getIssuesbyJql(jql) {
  const resource = "/rest/api/3/search";
  const maxResults = 100;
  let startAt = 0;
  let total = 0;

  try{
    if (jql==""){
      throw new Error("jqlが未設定です。設定して実行してください。");
    }

    let issues = [];
    do {
      let params = {
        "maxResults" : maxResults, // 1回に最大100件まで取得可能
        "startAt" : startAt,       // オフセット
        "jql" : jql                // JQL
      }
      const response = sendRequest("GET", resource, params);
      //console.log(response);
      for (const issue of response.issues){
        issues.push(issue);
      }
      total = response.total;
      startAt += maxResults;
    } while(total>startAt)

    return issues;
  } catch(e) {
    return {"error": e.message};
  }
}
```

例えば、`開発A` というプロジェクトの `Epic` を `更新日` で降順にソートして取得したい場合、以下の JQL を `getIssuesbyJql` に指定して実行すると取得できます。

```sql:JQL
project = 開発A AND issuetype = Epic ORDER BY updated DESC
```

取得した課題 `issues` を加工し、お好みの形式で Google スプレッドシートに書き出して課題リストを作成してください。
また、一定時間で自動的に課題リストを更新したい場合は、GAS の時間トリガーを使用することで実現可能です。

# まとめ

今回は Jira Cloud REST API と GAS を使用し、 JQL で指定した条件にマッチする課題を抽出する方法を紹介しましたが、如何だったでしょうか？

余談になりますが、Jira Cloud には自動化機能があり、Web リクエストを送信することができますので、REST API を自動化機能で使用することで、例えばプロジェクトの自動生成などが可能です。

以前、Jira Cloud の自動化機能に関する記事を書いているので、ご興味があればこちらもご確認ください。

https://qiita.com/cemaimai/items/0ba5bbc0c61e6a805965

最後まで読んでいただきありがとうございました！

# 参考サイト
- [The Jira Cloud Platform REST API | Atlassian Developer](https://developer.atlassian.com/cloud/jira/platform/rest/v3/intro/)