GraphQL APIでproject(beta)の紐つくissueを集計してみた

## はじめに
人力作業が嫌い、無くしたいと言う願望から
GraphQLを使うための備忘録
GASで集計結果をgoogle sheetに書き込む(略)
今までのgithub apiではproject(beta)が取れない

## 手順
1. Github Personal access tokens発行
1. queryを作成する（もしくはmutations）
    - [リファレンス](https://docs.github.com/ja/graphql/reference/objects)確認しながら必要なjson内容を組み込む
1. queryを```https://api.github.com/graphql```APIで投げてquery通りのjsonが返ってくる
1. json stringを解析して結果出力

### Github Personal access tokensについて
これはgithubのsetting画面から操作するだけ。
- Organizationの認証とか権限の設定とかあるかもしれない
- 今回はclassicのtokenを作ってrepoとprojectの権限を付与しました。

### 作り方
ここで初めて触った人は少し苦労するかもしれませんが構成や要件それぞれなので本当にリファレンスみてそれを理解した方が自由に組める。

##### 今回の要件
- 集計したいissueは全て同じrepositoryにある
- 集計対象issueは特定のlabelが付く
- 集計対象issueは特定のproject(beta)に紐づかれている
- 対象issueの特定属性で合計値をassigneeごとに集計したい

##### まずはコード
```
const variables = {
    org: "Your Org",
    repo: "Your Repo",
    assignee: "Your Assignee",
    label: "Your Label",
    since: "issue最後更新時間をfilterしている"
  };
  const query = `
    query($org: String!, $repo: String!, $label: String!, $assignee: String, $since: DateTime!) {
      repository(owner: $org, name: $repo) {
        issues(last: 100, filterBy: {labels: [$label], since: $since, assignee: $assignee}) {
          totalCount
          nodes {
            number
            title
            url
            projectItems(first: 1) {
              nodes{
                filterColumn: fieldValueByName(name: "filterかけたい項目名") {
                    ... on ProjectV2ItemFieldIterationValue{
                      title
                    }
                }
                cost: fieldValueByName(name: "集計したい値") {
                    ... on ProjectV2ItemFieldNumberValue{
                      number
                    }
                }
              }
            }
          }
        }
      }
    }
  `;

  const options = {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${Your token}`,
      "Accept": "application/json",
      "Content-Type": "application/json"
    },
    payload: JSON.stringify({ query: query, variables: variables })
  };

  const response = UrlFetchApp.fetch('https://api.github.com/graphql', options);
  const issues = JSON.parse(response.getContentText());
```
#### 今回使ったobject
- [repository](https://docs.github.com/ja/graphql/reference/objects#repository)
- [issueconnection(issuesの中身)](https://docs.github.com/ja/graphql/reference/objects#issueconnection)
- [issue](https://docs.github.com/ja/graphql/reference/objects#issue)
- [ProjectV2ItemConnection(projectItemsの中身)](https://docs.github.com/ja/graphql/reference/objects#projectv2itemconnection)
- [ProjectV2Item](https://docs.github.com/ja/graphql/reference/objects#projectv2item)
- [ProjectV2ItemFieldValue(fieldValueByNameで取得した値の汎用型)](https://docs.github.com/ja/graphql/reference/unions#projectv2itemfieldvalue)
    - 実際の型はprojectのcustom Fieldから各パラメータの`Field type`見ながらcastする

##### 細かいポイント
- queryの変数について
    - 変数声明仕方```$+変数名```: ```型```で使う時は```$+変数名```
    - ```!```がつくと```non null```、付かない場合は```nullable```
    - object型入力はJson objectを引数として渡せます(例:`filterBy`のところ)
    - 声明した変数は```variables```objectに渡すと```query```に適応されます
- queryの中身について
    - **リファレンスから欲しいobjectを探し出しましょう**
    - 全てのobjectは```Node```を継承している
    - ```nodes```がArrayになる
    - arrayにアクセスするためには基本paging想定されます
        - 件数が少ない場合なら決め打ちで最大以上の件数分とってこれるが、多い場合は[PageInfo](https://docs.github.com/ja/graphql/reference/objects#pageinfo)を使ってpagingの仕組みをくむ
    - ```... on {Type}```が型filter、Arrayの中は汎用型が含まれる場合が多いからたくさん使う
- 認証について
    - headderの```Authorization```に```token```を渡す
    - ```token```の前に固定文字**Bearer**+**スペース**を忘れずに
    - 認証失敗すると`401`の`Bad credentials`になる
- レスポンスの使い方について
    - query以下の構造は記載した階層の通りでjson string返却される。
    - 例えば```filterかけたい項目名```をアクセスしたい場合は`issues.data.repository.issues.nodes[0].projectItems.nodes[0].filterColumn.title`

## 最後に
queryのデバグは割と簡単で`response.getContentText()`の中にエラーの詳細が返却されるのでそれをLogに出せばなんとかなるはず。
ほぼ自分見返す用ですが誰かに役立つと嬉しいです。