module JsonApiClient

TODO: conform to JSON API spec v1.0 jsonapi.org

Public Class Methods

fetch_all_pages(url:) click to toggle source

Fetches all pages for the given url. Information about pagination is provided in the Link header of an API call. tools.ietf.org/html/rfc5988 Example: Link: <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"

Returns JSON parsed response body

# File lib/json_api_client.rb, line 18
def JsonApiClient.fetch_all_pages(url:)
  headers, body = fetch_page(url: url)

  return nil if headers.nil? || body.nil?

  resources = JSON.parse(body)
  while url = next_page(headers: headers) do
    headers, body = fetch_page(url: url)
    resources << JSON.parse(body)
  end

  resources
end
fetch_page(url:) click to toggle source

Fetches a single page for the given url.

Returns a header, body pair of response

# File lib/json_api_client.rb, line 35
def JsonApiClient.fetch_page(url:)
  begin
    res = open(URI(url))
    [res.meta, res.read]
  rescue OpenURI::HTTPError => e
    process_error e
  end
end
next_page(headers:) click to toggle source

Finds next page from ‘Link’ response header

Returns a url of next page

# File lib/json_api_client.rb, line 81
def JsonApiClient.next_page(headers:)
  link = (headers["Link"] || "").split(', ').map do |link|
    href, name = link.match(/<(.*?)>; rel="(\w+)"/).captures
    return href if name == 'next'
  end

  nil
end
process_error(error) click to toggle source

Abstract OpenURI::HTTPErrors so we can:

  • provide more information

  • replace it later if needed

Raises new error containing any additional valuable information.

# File lib/json_api_client.rb, line 49
def JsonApiClient.process_error(error)
  headers = error.io.meta
  if error.message == '404 Not Found'
    raise JsonApiClient::NotFound.new(headers: headers, message: 'Not Found')
  elsif error.message == '403 Forbidden'
    # Rate Limit Remaining examples: http://stackoverflow.com/a/16022625
    ratelimit_remaining = headers["x-ratelimit-userremaining"] ||
                          headers["x-ratelimit-remaining"] ||
                          headers["x-rate-limit-remaining"]
    
    if ratelimit_remaining.to_i == 0
      ratelimit_reset = headers["x-ratelimit-reset"] ||
                        headers["x-rate-limit-reset"] ||
                        headers["x-ratelimit-userreset"]
      message = 'Rate limit exceeded.'

      if ratelimit_reset
        ratelimit_reset_time = Time.at(ratelimit_reset.to_i)
        message += " Try again after #{ratelimit_reset_time}."
      end
      api_error = JsonApiClient::RateLimitExceeded.new(headers: headers,
                                                       message: message)
      raise api_error
    end
  end
  
  raise JsonApiClient::Error.new(headers: headers, message: error.message)
end