lib/gds_api/response.rb
require 'json' require 'forwardable' require 'rack/cache' module GdsApi # This wraps an HTTP response with a JSON body. # # Responses can be configured to use relative URLs for `web_url` properties. # API endpoints should return absolute URLs so that they make sense outside of the # GOV.UK context. However on internal systems we want to present relative URLs. # By specifying a base URI, this will convert all matching web_urls into relative URLs # This is useful on non-canonical frontends, such as those in staging environments. # See: https://github.com/alphagov/wiki/wiki/API-conventions for details on the API conventions # # Example: # # r = Response.new(response, web_urls_relative_to: "https://www.gov.uk") # r['results'][0]['web_url'] # => "/bank-holidays" class Response extend Forwardable include Enumerable def_delegators :to_hash, :[], :"<=>", :each, :dig def initialize(http_response, options = {}) @http_response = http_response @web_urls_relative_to = URI.parse(options[:web_urls_relative_to]) if options[:web_urls_relative_to] end def raw_response_body @http_response.body end def code # Return an integer code for consistency with HTTPErrorResponse @http_response.code end def headers @http_response.headers end def expires_at if headers[:date] && cache_control.max_age response_date = Time.parse(headers[:date]) response_date + cache_control.max_age elsif headers[:expires] Time.parse(headers[:expires]) end end def expires_in return unless headers[:date] age = Time.now.utc - Time.parse(headers[:date]) if cache_control.max_age cache_control.max_age - age.to_i elsif headers[:expires] Time.parse(headers[:expires]).to_i - Time.now.utc.to_i end end def cache_control @cache_control ||= Rack::Cache::CacheControl.new(headers[:cache_control]) end def to_hash parsed_content end def parsed_content @parsed_content ||= transform_parsed(JSON.parse(@http_response.body)) end def present?; true; end def blank?; false; end private def transform_parsed(value) return value if @web_urls_relative_to.nil? case value when Hash Hash[value.map { |k, v| # NOTE: Don't bother transforming if the value is nil if 'web_url' == k && v # Use relative URLs to route when the web_url value is on the # same domain as the site root. Note that we can't just use the # `route_to` method, as this would give us technically correct # but potentially confusing `//host/path` URLs for URLs with the # same scheme but different hosts. relative_url = @web_urls_relative_to.route_to(v) [k, relative_url.host ? v : relative_url.to_s] else [k, transform_parsed(v)] end }] when Array value.map { |v| transform_parsed(v) } else value end end end end