REST API で Confluence Cloud の総ページ数を知る

## 動機
サーバ版のConfluenceであればAdd-onやUser Macroでページ数を知る方法があります[^1]が、クラウド版のConfluenceではページ数を簡単に取得する方法が提供されていません。そのためAtlassian Answersでも [For Confluence Cloud, how do I find the total number of pages?](https://answers.atlassian.com/questions/10417309/for-confluence-cloud-how-do-i-find-the-total-number-of-pages) などの質問をよく見かけます。

そこで本エントリではConfluence Cloudでページ数などを取得する方法を紹介します。


## 解決法
[Confluence Cloud REST API](https://docs.atlassian.com/confluence/REST/latest/#space-spaces) を用います。

コードで示すのが手っ取り早いので、下掲します[^jq]。試しに次の3つのデータを取得してみます。

* 総ページ数
* 総ユーザー数
* スペース数

ここではスペース横断でアクセスするために *admin* ユーザーを使用してREST APIにリクエストしています。

※パスワードは [API tokens](https://id.atlassian.com/manage/api-tokens) を発行して利用することをおすすめします。


```bash:confluence_cloud_rest_api/example.sh
# Be sure that a user has a privilege to access all the Confluence spaces
A_USER="admin"
A_PASSWD="__YOUR_PASSWORD_HERE__"
INSTANCE_HOST="__YOUR_SUBDOMAIN_HERE__.atlassian.net"
 
# Total number of pages
curl -sGLu ${A_USER}:${A_PASSWD} "https://${INSTANCE_HOST}/wiki/rest/api/search" --data-urlencode 'cql=type IN (blogpost, page)' --data-urlencode 'limit=0' | jq '.totalSize'
 
# Total number of users
curl -sLu ${A_USER}:${A_PASSWD} "https://${INSTANCE_HOST}/wiki/rest/api/group/confluence-users/member?limit=1000" | jq '.size'
 
# Number of spaces (Assumed to be less than 1000)
curl -sLu ${A_USER}:${A_PASSWD} "https://${INSTANCE_HOST}/wiki/rest/api/space?limit=1000" | jq '.size'
```

特に `rest/api/search` はCQL[^cql]が利用できるため強力ですね。


## 制限
REST APIには次のような制限があります。ただし、1つめの制限に関してはある程度ワークアラウンドがあるため後述します。

* JQL, CQL で統計用途のクエリが使えない
    * SUMなどの集計関数[^4]や、SQLの `GROUP BY` 相当の機能[^5]がサポートされていない
    * これは本来 JQL, CQL が issue や page のフィルタリングとして機能提供されているため仕方がない側面もあるが、例えば「作業率 (*workratio*)」などのビルトインのフィールドを用いることである程度、業務要件は満たすこともできる[^field]
* ブラウザからのリクエストができない
    * CSRF防止のため `*.atlassian.net` 以外からのXMLHttpRequestが禁止されている[^2] [^3]
    * XSS防止のため `*.atlassian.net` でのHTMLの埋め込みが禁止されている[^6] [^7]
* REST APIで取得できない情報がある
  * 例えば、ページビュー数 (PV) を取得するエンドポイントはない


## それでもやはり `GROUP BY` で集計したい
前節で制限をあげましたが、それでも「スペースごとのページ数」などは取得したいところです。
そこでワークアラウンドとして、愚直に複数クエリを投げることで `GROUP BY` での集計を行います。

#### 例: スペースごとのページ数
前節の例よりも煩雑になります（個人的にはshellとしてギリギリ許容範囲の可読性です）。

```bash:confluence_cloud_rest_api/count_pages_by_space.sh
A_USER="admin"
A_PASSWD="__YOUR_PASSWORD_HERE__"
INSTANCE_HOST="__YOUR_SUBDOMAIN_HERE__.atlassian.net"
SPACE_LIMIT="1000"

function space_keys {
  curl -sGLu ${A_USER}:${A_PASSWD} "https://${INSTANCE_HOST}/wiki/rest/api/space"  --data-urlencode "limit=${SPACE_LIMIT}" | jq -r '.results[].key'
}

function page_num_by_space {
  for SPACE in $(space_keys); do
    NUM_PAGES=$(curl -sGLu ${A_USER}:${A_PASSWD} "https://${INSTANCE_HOST}/wiki/rest/api/search" --data-urlencode "cql=type IN (blogpost, page) AND space='${SPACE}'" --data-urlencode 'limit=0' | jq '.totalSize')
    echo "${SPACE}\t${NUM_PAGES}"
  done
}

page_num_by_space | sort -rk2
```

標準出力として下記のように tsv 形式で表示されます。

```text:STDOUT.tsv
DEF     5
BIJP    2
~admin  1
```

このように複数クエリを組み合わせることで様々な応用が効くようになります。例えば「ユーザーごとに `contributor` になっているページ数」なども取得できるでしょう。


[^1]: 例えば [Count pages in space](https://confluence.atlassian.com/display/DISC/Count+pages+in+space) など。
[^2]: [Cross Site Request Forgery \(CSRF\) protection changes in Atlassian REST \- Atlassian Documentation](https://confluence.atlassian.com/kb/cross-site-request-forgery-csrf-protection-changes-in-atlassian-rest-779294918.html)
[^3]: [XSRF check failed when calling Cloud APIs \- Atlassian Documentation](https://confluence.atlassian.com/cloudkb/xsrf-check-failed-when-calling-cloud-apis-826874382.html)
[^4]: [\[JRA\-28164\] Ability to calculate values in JQL \- Atlassian JIRA](https://jira.atlassian.com/browse/JRA-28164)
[^5]: [\[JRA\-23516\] Support Group By in JQL \- Atlassian JIRA](https://jira.atlassian.com/browse/JRA-23516)
[^6]: [Restricted functions in Confluence Cloud \- Atlassian Documentation](https://confluence.atlassian.com/confcloud/restricted-functions-in-confluence-cloud-734070955.html)
[^7]: [\[CLOUD\-5674\] Enable Custom HTML plugin for Confluence Cloud \- Atlassian JIRA](https://jira.atlassian.com/browse/CLOUD-5674)
[^jq]: `jq` コマンドについては https://stedolan.github.io/jq/ を参照してください。
[^field]: 詳しくは [フィールドのリファレンス](https://ja.confluence.atlassian.com/display/JIRASOFTWARECLOUD/Advanced+searching+-+fields+reference#Advancedsearching-fieldsreference-WorkRatioworkRatioWorkratio) を参照してください。
[^cql]: [Advanced Searching using CQL \- Atlassian Developers](https://developer.atlassian.com/confcloud/advanced-searching-using-cql-39985862.html)