lib/carsensor/api.rb in carsensor-api-0.0.1.alpha.1 vs lib/carsensor/api.rb in carsensor-api-0.0.1.alpha.2
- old
+ new
@@ -1,21 +1,27 @@
# frozen_string_literal: true
+require 'logger'
require 'uri'
require 'open-uri'
require 'carsensor/api/version'
require 'rack/utils'
+require 'retriable'
module Carsensor
- # A thin wrapper for Carsensor Web API at (https://webservice.recruit.co.jp/carsensor/reference.html
+ # A thin wrapper for Carsensor Web API at https://webservice.recruit.co.jp/carsensor/reference.html
class API
# Exception class for errors which would occur during invocation of Carsensor API
class Error < StandardError
# @param message [String] Message for the exception
# @param code [Integer,String] Error code of API itself or error status of HTTP
- def initialize(message:, code:)
- super('%s(code: %s)' % [message, code])
+ def initialize(message:, code: nil)
+ if code
+ super('%s(code: %s)' % [message, code])
+ else
+ super(message)
+ end
end
end
ENDPOINT_BASE_URI = URI('http://webservice.recruit.co.jp')
@@ -23,14 +29,16 @@
module Metadata
attr_accessor :metadata
end
# @param key [String] The API key
- def initialize(key:)
+ def initialize(key:, logger: Logger.new(STDOUT), tries: 3)
raise ArgumentError, 'key: must be a String' unless key.is_a?(String)
@key = key
+ @logger = logger
+ @tries = tries
end
# Returns bodies for given criterion
#
# @param query [Hash] options to specify criterion
@@ -95,31 +103,55 @@
end
private
def call_api(root, path, **query)
- (method(:build_uri) >> method(:read_uri) >> method(:parse_body) >> method(:extract_result).curry[root]).(path, query)
+ Retriable.retriable(tries: @tries, on: [Error], on_retry: on_retry_proc(path, query)) do
+ (method(:build_uri) >> method(:read_uri) >> method(:parse_body) >> method(:extract_result).curry[root]).(path, query)
+ end
end
+ def on_retry_proc(path, **query)
+ lambda do |exception, _try, _elapsed_time, _next_interval|
+ return unless @logger
+
+ @logger.info(retry_message(exception, path, query))
+ end
+ end
+
+ def retry_message(exception, path, **query)
+ query_string = build_query_string(query)
+ request = query_string.length.zero? ? path : path + '?' + query_string
+ '%s %s %s' % [exception.class, exception.message, request]
+ end
+
+ def build_query_string(**query)
+ Rack::Utils.build_query(key: @key, format: 'json', **query.transform_values {|v| v.is_a?(Array) ? v.join(',') : v })
+ end
+
def build_uri(path, **query)
ENDPOINT_BASE_URI.dup.tap do |u|
u.path = path
- u.query = Rack::Utils.build_query(key: @key, format: 'json', **query.transform_values {|v| v.is_a?(Array) ? v.join(',') : v })
+ u.query = build_query_string(query)
end
end
def read_uri(uri)
uri.read
rescue OpenURI::HTTPError => e
status = e.io.status
raise Error.new(message: status[1], code: status[0])
+ rescue SocketError => e
+ raise Error.new(message: e.message)
end
def parse_body(body)
JSON.parse(body, symbolize_names: true).tap do |data|
api_error = data.dig(:results, :error, 0)
raise Error.new(**api_error) if api_error
end
+ rescue JSON::ParserError => e
+ raise Error.new(message: e.message)
end
def extract_result(root, data)
(data.dig(:results, root) || []).tap do |result|
result.extend(Metadata)