GitHub Enterprise Server APIは、開発者が利用できる情� �を豊富に提供します。 ほとんどの� �合は、要求している情� �が多すぎるということに気付くかもしれません。サーバーに� 担をかけすぎないため、API は自動的にリクエストされたアイテ� をページネーションします。
このガイドでは、 Search APIを呼び出し、ページネーションを使って結果を反復処理します。 このプロジェクトの完全なソースコードは、platform-samplesリポジトリにあります。
Note: The following guide uses the REST API for GitHub.com.
-
Use
http(s)://[hostname]/api/v3
to access the API for GitHub Enterprise Server. -
The guide specifies usernames and repositories that may not exist on GitHub Enterprise Serverインスタンス. You may need to use different names to see similar output.
ページネーションの基本
はじめに、ページネーションされたアイテ� の受け取りについて、いくつかの事実を知っておくことが重要です。
- 呼び出す API によって、応答するデフォルト値が異なります。 パブリックリポジトリのリスト化を呼び出すと、ページネーションされて提供されるのは 1 セットで 30 アイテ� ですが、GitHub Search APIを呼び出すと 1 セットで 100 アイテ� となります。
- 受け取るアイテ� の数は指定できます (最大 100 まで)。
- た� し、技術的な理由により、すべてのエンドポイントが同じ動作をするわけではありません。 たとえば、イベントでは受け取るアイテ� 数の最大値を設定できません。 特定のエンドポイントにおけるページネーションされた結果の処理方法については、必ずドキュメントをお読みく� さい。
ページネーションに関する情� �は、API呼び出しのリンクヘッダに記載されています。 たとえば、検索APIにcurlでリクエストを行って、Mozilla プロジェクトで addClass
というフレーズを何回使っているか調べましょう。
$ curl -I "https://api.github.com/search/code?q=addClass+user:mozilla"
-I
パラメータは、実際のコンテンツではなくヘッダのみを扱うことを示します。 結果を調べると、Linkヘッダの中に以下のような情� �があることに気付くでしょう。
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=2>; rel="next",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last"
さて、細かく見ていきましょう。 rel="next"
には、次のページがpage=2
� と書かれています。 これは納得できる話です。というのも、デフォルトでは、すべてのページネーションされたクエリは1
ページから始まります。rel="last"
には追� 情� �があり、最後のページは34
ページになると書かれています。 つまり、addClass
で利用できる情� �はあと33ページあるということですね。 よし!
提供されたこのリンク関係に常に依存しましょう。 URLを推測したり、自分で構築したりしないでく� さい。
ページ間の移動
さて、受信するページ数がわかったので、ページを移動して結果の利用を開始できます。 これを行うには、page
パラメータを渡します。 デフォルトでは、 page
は常に1
から始まります。 14ページまでジャンプして、どうなるか見てみましょう。
$ curl -I "https://api.github.com/search/code?q=addClass+user:mozilla&page=14"
ここでもう一度リンクヘッダを見てみます。
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&page=15>; rel="next",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=34>; rel="last",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=1>; rel="first",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&page=13>; rel="prev"
予想通りrel="next"
は15で、rel="last"
は34のままです。 しかし今度は少し情� �が増えています。rel="first"
は、、最初のページのURLを示しています。さらに重要なこととして、rel="prev"
は前のページのページ番号を示しています。 この情� �を用いて、APIの呼び出しでリストの最初、前、次、最後にユーザがジャンプできるUIを構築できるでしょう。
受け取るアイテ� 数の変更
per_page
パラメータを渡すことで、各ページが返すアイテ� 数を最大100まで指定できます。 addClass
について50アイテ� を要求してみましょう。
$ curl -I "https://api.github.com/search/code?q=addClass+user:mozilla&per_page=50"
ヘッダのレスポンスに何が起こるかに注目してく� さい。
Link: <https://api.github.com/search/code?q=addClass+user%3Amozilla&per_page=50&page=2>; rel="next",
<https://api.github.com/search/code?q=addClass+user%3Amozilla&per_page=50&page=20>; rel="last"
ご想像の通り、rel="last"
情� �には、最後のページが20になったと書かれています。 これは、結果のページごとに、より多くの情� �を要求しているからです。
情� �の取得
ページネーションを扱うため� けに低レベルのcurlを呼び出したくはないでしょうから、上記で説明したことをすべて行うような、ちょっとしたRubyスクリプトを書いてみましょう。
いつものように、まずはGitHub's Octokit.rb Rubyライブラリを読み込んで、個人アクセストークンを渡す必要があります。
require 'octokit'
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
client = Octokit::Client.new :access_token => ENV['MY_PERSONAL_TOKEN']
次に、Octokitのsearch_code
メソッドを使用して、検索を実行します。 curl
を使用する� �合と異なり、結果の数をすぐ取得することもできるので、そうしてみましょう。
results = client.search_code('addClass user:mozilla')
total_count = results.total_count
さて、リンクヘッダの page=34>; rel="last"
情� �と同様に、最後のページ番号を取得しましょう。 Octokit.rb は、「ハイパーメディアリンク関係」という実装を通じてページネーション情� �をサポートしています。 それについて詳しくは扱いませんが、results
変数の各要� にはrels
というハッシュがあり、そのハッシュには結果に応じて:next
、:last
、:first
、:prev
といった情� �が含まれること� け触れておけばここでは十分でしょう。 また、生成されるURLについての情� �も含まれ、rels[:last].href
を呼び出すことにより取得できます。
これがわかったら、最後の結果のページ番号を取得し、ユーザにすべての情� �を表示しましょう。
last_response = client.last_response
number_of_pages = last_response.rels[:last].href.match(/page=(\d+).*$/)[1]
puts "There are #{total_count} results, on #{number_of_pages} pages!"
最後に、結果を反復処理しましょう。 for i in 1..number_of_pages.to_i
のループを使うこともできますが、ここではrels[:next]
ヘッダに従って、各ページから情� �を取得します。 簡単にするため、各ページの最初の結果の、ファイルパス� けを取得します。 これを行うには、ループが必要です。そして各ループの最後で、rels[:next]
情� �に従って、次のページのデータセットを取得します。 取得するrels[:next]
情� �がなくなる (言い換えれば、rels[:last]
に到着する) と、ループは終了します。 このようになるでしょう。
puts last_response.data.items.first.path
until last_response.rels[:next].nil?
last_response = last_response.rels[:next].get
puts last_response.data.items.first.path
end
Octokit.rb では、ページあたりのアイテ� 数を変更することは非常に簡単です。 per_page
オプションのハッシュを初期クライアント構築に渡す� けです。 その後、コードはそのままになっているはずです。
require 'octokit'
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
client = Octokit::Client.new :access_token => ENV['MY_PERSONAL_TOKEN']
results = client.search_code('addClass user:mozilla', :per_page => 100)
total_count = results.total_count
last_response = client.last_response
number_of_pages = last_response.rels[:last].href.match(/page=(\d+).*$/)[1]
puts last_response.rels[:last].href
puts "There are #{total_count} results, on #{number_of_pages} pages!"
puts "And here's the first path for every set"
puts last_response.data.items.first.path
until last_response.rels[:next].nil?
last_response = last_response.rels[:next].get
puts last_response.data.items.first.path
end
ページネーションリンクの作成
通常、ページネーションの目的は可能なすべての結果を連結することではなく、以下のようなナビゲーションを生成することです。
ここで起こりそうなことを小さなモデルを使って描いてみましょう。
以下の最初の呼び出しでページネーションされた結果のnumber_of_pages
を所得できることは、上記のコードからすでに知っています。
require 'octokit'
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
client = Octokit::Client.new :access_token => ENV['MY_PERSONAL_TOKEN']
results = client.search_code('addClass user:mozilla')
total_count = results.total_count
last_response = client.last_response
number_of_pages = last_response.rels[:last].href.match(/page=(\d+).*$/)[1]
puts last_response.rels[:last].href
puts "There are #{total_count} results, on #{number_of_pages} pages!"
ここから、数値ボックスを美しい ASCII 文字で構築できます。
numbers = ""
for i in 1..number_of_pages.to_i
numbers << "[#{i}] "
end
puts numbers
ランダ� な番号を生成して、このボックスのいずれかをクリックするユーザをシミュレートしてみます。
random_page = Random.new
random_page = random_page.rand(1..number_of_pages.to_i)
puts "A User appeared, and clicked number #{random_page}!"
さて、ページ番号があるので、 :page
オプションを渡すことにより、Octokitで各ページを明示的に取得できます。
clicked_results = client.search_code('addClass user:mozilla', :page => random_page)
もっと見� �えをよくしたければ、前のページと次のページも取得して、戻る (<<
) と進む (>>
) のリンクも生成できます。
prev_page_href = client.last_response.rels[:prev] ? client.last_response.rels[:prev].href : "(none)"
next_page_href = client.last_response.rels[:next] ? client.last_response.rels[:next].href : "(none)"
puts "The prev page link is #{prev_page_href}"
puts "The next page link is #{next_page_href}"