httrでBacklog APIをコールしてggplotで可視化

## 概要

普段Rを使っているとライブラリ経由でのデータ取り込みはあるもののWebAPIを利用することが少ないので、この機会にhttrライブラリを整理してみます。
最終的にはBacklogのAPIをコールしてggolotで可視化します。

## httrとは
httrはRのHTTPクライアントで、HTTPリクエストとして以下のメソッドが用意されています。

- GET()
- HEAD()
- POST()
- PATCH()
- PUT()
- DELETE()

これらの関数を利用してリクエストを投げる場合は第一引数にURLを指定します。

```R
GET("http://httpbin.org/get")

## Response [http://httpbin.org/get]
##   Date: 2023-02-20 11:52
##   Status: 200
##   Content-Type: application/json
##   Size: 367 B
## {
##   "args": {}, 
##   "headers": {
##     "Accept": "application/json, text/xml, application/xml, */*", 
##     "Accept-Encoding": "deflate, gzip", 
##     "Host": "httpbin.org", 
##     "User-Agent": "libcurl/7.85.0 r-curl/4.3.2 httr/1.4.3", 
##     "X-Amzn-Trace-Id": "Root=1-63f35f08-181377b44f1de4550b6f93b9"
##   }, 
##   "origin": "59.138.157.99", 
## ...
```

### HTTPリクエスト

以下のような形でquery引数を指定することでURLパラメータを渡すことができます。

```R
GET("http://httpbin.org/get", 
  query = list(key1 = "value1", key2 = "value2")
)
```

URL文字列を作る際にパラメータをpaste0で文字列結合せずに済むので便利ですね。

設定関数を引数として渡すとカスタムヘッダー等の設定ができます。

```R
GET("http://httpbin.org/get", add_headers(Name = "Hadley"))
```

設定周りとしては以下の関数が用意されています。

- set_cookies()
- add_headers()
- authenticate()
- use_proxy()
- verbose()
- timeout()
- content_type()
- accept()
- progress()


POSTでリクエストボディを載せる場合はbody引数が利用できます。
リクエストパラメータの時と同様にリスト型として `key = value` の形で値を渡します。

```R
url <- "http://httpbin.org/post"
body <- list(a = 1, b = 2, c = 3)

# Form encoded
POST(url, body = body, encode = "form")
# Multipart encoded
POST(url, body = body, encode = "multipart")
# JSON encoded
POST(url, body = body, encode = "json")
```
encode引数でペイロードのエンコード指定も可能です。

### HTTPレスポンス

HTTPレスポンスの情報全体はレスポンスオブジェクトとして取得することができます。

```R
GET("http://httpbin.org/get")

## Response [http://httpbin.org/get]
##   Date: 2023-02-17 00:31
##   Status: 200
##   Content-Type: application/json
##   Size: 367 B
## {
##   "args": {}, 
##   "headers": {
##     "Accept": "application/json, text/xml, application/xml, */*", 
##     "Accept-Encoding": "deflate, gzip", 
##     "Host": "httpbin.org", 
##     "User-Agent": "libcurl/7.85.0 r-curl/4.3.2 httr/1.4.3", 
##     "X-Amzn-Trace-Id": "Root=1-63eecade-70aabdda6cdcce436206798a"
##   }, 
##   "origin": "59.138.157.99", 
## ...
```

このレスポンスオブジェクトからレスポンスボディを取り出す場合にはcontent関数を利用します。

```R
content(GET("http://httpbin.org/get"), "parsed")

## $args
## named list()
## 
## $headers
## $headers$Accept
## [1] "application/json, text/xml, application/xml, */*"
## 
## $headers$`Accept-Encoding`
## [1] "deflate, gzip"
## 
## $headers$Host
## [1] "httpbin.org"
## 
## $headers$`User-Agent`
## [1] "libcurl/7.85.0 r-curl/4.3.2 httr/1.4.3"
## 
## $headers$`X-Amzn-Trace-Id`
## [1] "Root=1-63eecb3f-79b211903e70ef0f68c62441"
## 
## 
## $origin
## [1] "59.138.157.99"
## 
## $url
## [1] "http://httpbin.org/get"
```

レスポンスがJSONの場合にはcontent関数の第二引数に"parsed"を指定することで、パースされた結果が名前付きリストとして返ってきます。

以上が簡単なhttrの説明となります。


## Backlog API

BacklogのREST APIでは課題一覧の取得ができますので、これを利用することにします。

https://developer.nulab.com/ja/docs/backlog/api/2/get-issue-list/

今回は `/api/v2/issues` のエンドポイントに対してリクエストパラメータとしてapiKey、projectId[]、count、offsetを指定します。

### 認証

管理画面上でAPI Keyを発行し、このキーをURLパラメータにセットして認証用に利用します。

![cap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/ef8b2113-de72-c869-d841-05aa9eef2c4f.png)


https://developer.nulab.com/ja/docs/backlog/auth/


なお、各APIのレート制限もAPI経由で確認ができるようです。

https://developer.nulab.com/ja/docs/backlog/api/2/get-rate-limit/#



## 実践
httrとBacklog APIの仕様を整理できたので、早速利用してみましょう。

### ライブラリの読み込みと変数宣言

```R 
library(tidyverse)
library(httr)
library(lubridate)

## エンドポイント関連
base_uri <- "https://example.com/api/v2/"
project_id <- "PROJECT_ID"
max_request_num <- 5
offset <- 0
threshold <- 100
api_key <- "YOUR_API_KEY"

request_uri <- paste0(base_uri, "issues")

## 空の値で宣言
merged_response <- list()
df_response <- tibble()
```

- project_id
  - BacklogのプロジェクトIDはプロジェクト設定ページのページURLから参照可能です。
      - 例：`https://example.com/EditProject.action?project.id=12345`
- max_request_num
  - forループを回す最大の回数です。BacklogのAPIでは最大100件しか取得できないため、ループ処理で複数回APIコールします。
- offset
    - オフセット値です。
- threshold
    - 一度に取得する件数です。（最大で100件）
- api_key 
    - 管理画面から発行したAPI KEYです。
- request_uri
    - 今回は課題一覧を取得するため、エンドポイントの"issues"を指定します。


### APIコールとデータフレームの準備

forループでぐるぐる回して課題一覧を取得します。

```R
## 複数回に分けてAPIをコール
for (i in 1:max_request_num) {
  response <- content(
    GET(request_uri,
      query = list(apiKey = api_key, "projectId[]" = project_id, count = threshold, offset = offset)
    ),
    "parsed"
  )
  if (length(response) == 0) {
    break
  }
  offset <- offset + threshold
  merged_response <- c(merged_response, response)
}

## listからデータフレームを作成
df_response <- data.frame(do.call(rbind, merged_response)) %>%
  select(created, summary) %>%
  mutate(date = unlist(created), title = unlist(summary)) %>%
  mutate(date = as.Date(date)) %>%
  mutate(date = make_date(year(date), month(date), 01))
```

今回は取得したデータから「課題が発行された日付（created）」と「課題名（summary）」をデータフレームに格納しています。

### 可視化

用意したデータフレームを利用してggplot2で可視化します。

```R
theme_set(theme_gray(base_family = "HiraKakuProN-W3"))

ggplot(df_response, aes(x = date)) +
  geom_line(stat = "count") +
  scale_x_date(date_labels = "%b %y", date_breaks = "1 month") +
  theme(axis.text.x = element_text(angle = 90)) + #テキストを90度回転させてXラベル見やすくする
  ggtitle("課題の件数の推移") +
  xlab("") +
  ylab("件数")
```

![Rplot07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/4ff614c5-f2e1-c532-a8d7-1c2a028320bc.png)


任意のBacklogプロジェクトの課題の件数が時系列として可視化できました。

## おわりに
Rから簡単にBacklogのREST APIを利用できました。

今回は課題の件数の推移を可視化しましたが、課題に含まれるテキストも抽出することができるため、例えばテキストマイニングとしても活用できるかもしれません。

個人的にはカスタマーサポート周りに関して「問い合わせ件数」であったり「問い合わせの内容（単語）の統計量」を時系列で可視化できたら面白そうだなと感じています。
（社内向けのBI環境をShiny Serverで構築したいな・・

## 関連ページ

https://cran.r-project.org/web/packages/httr/vignettes/quickstart.html

https://developer.nulab.com/ja/docs/backlog/