class Network::Client
This class is simple JSON
client that is meant to be initialized with a single URI. Subsequent calls should target endpoints/paths of that URI.
Constants
- DEFAULT_HEADERS
- LOG_TAG
Stamp in front of each log written by client +@logger+.
- Response
The success response template.
Represents the return of rest-like methods holding two values: HTTP response code, and body (parsed as json if request type is json).
Attributes
Error list for stop and propagate strategy. Takes priority over +@errors_to_recover+. Do not assign ancestor error classes here that prevent retry for descendant ones.
Error list for retrying strategy. Initially contains common errors encountered usually in net calls.
Gives access to underlying NET::HTTP client instance.
Public Class Methods
Construct and prepare client for requests targeting endpoint
.
Parameters:¶ ↑
- endpoint
-
string
Uri for the host with schema and port. any other segment like paths will be discarded. - tries
-
integer
to specify how many is to repeat failed calls. Default is 2. - headers
-
hash
to contain any common HTTP headers to be set in client calls. - username
-
string
for HTTP basic authentication. Applies on all requests. Default to nil. - password
-
string
for HTTP basic authentication. Applies on all requests. Default to nil. - user_agent
-
string
Specifies the User-Agent header value when making requests.
User-Agent header value provided within headers
parameter in initialize
or on one of request methods will take precedence over user_agent
parameter.
Example:¶ ↑
require "network-client" github_client = Network::Client.new(endpoint: 'https://api.github.com') github_client.get '/emojis' #=> { "+1": "https://assets-cdn.github.com/images/icons/emoji/unicode/1f44d.png?v7", "-1": "https://assets-cdn.github.com/images/icons/emoji/unicode/1f44e.png?v7", ... }
# File lib/network/client.rb, line 63 def initialize(endpoint:, tries: 2, headers: {}, username: nil, password: nil, user_agent: 'Network Client') @uri = URI.parse(endpoint) @tries = tries set_http_client define_error_strategies set_default_headers(headers) set_basic_auth(username, password) set_bearer_auth set_token_auth set_logger set_user_agent(headers['User-Agent'] || user_agent) end
Public Instance Methods
Perform a delete request on the targeted client endpoint
.
Parameters:¶ ↑
- path
-
string
path on client's target host. - params
-
hash
request parameters to json encoded in request body. - headers
-
hash
set of http request headers.
Returns:¶ ↑
http response data contained in Response
struct.
# File lib/network/client.rb, line 149 def delete(path, params: {}, headers: {}) request_json :delete, path, params, headers end
Perform a get request on the targeted client endpoint
.
Parameters:¶ ↑
- path
-
string
path on client's target host. - params
-
request parameters to be url encoded. Can be
hash
or pair of valuesarray
. - headers
-
hash
set of http request headers.
Returns:¶ ↑
http response data contained in Response
struct.
# File lib/network/client.rb, line 89 def get(path, params: {}, headers: {}) request_json :get, path, params, headers end
# File lib/network/client.rb, line 153 def get_html(path, params: {}, headers: {}) raise NotImplementedError end
Perform a patch request on the targeted client endpoint
.
Parameters:¶ ↑
- path
-
string
path on client's target host. - params
-
hash
request parameters to json encoded in request body. - headers
-
hash
set of http request headers.
Returns:¶ ↑
http response data contained in Response
struct.
# File lib/network/client.rb, line 119 def patch(path, params: {}, headers: {}) request_json :patch, path, params, headers end
Perform a post request on the targeted client endpoint
.
Parameters:¶ ↑
- path
-
string
path on client's target host. - params
-
hash
request parameters to json encoded in request body. - headers
-
hash
set of http request headers.
Returns:¶ ↑
http response data contained in Response
struct.
# File lib/network/client.rb, line 104 def post(path, params: {}, headers: {}) request_json :post, path, params, headers end
# File lib/network/client.rb, line 157 def post_form(path, params: {}, headers: {}) raise NotImplementedError end
Perform a put request on the targeted client endpoint
.
Parameters:¶ ↑
- path
-
string
path on client's target host. - params
-
hash
request parameters to json encoded in request body. - headers
-
hash
set of http request headers.
Returns:¶ ↑
http response data cotained in Response
strcut.
# File lib/network/client.rb, line 134 def put(path, params: {}, headers: {}) request_json :put, path, params, headers end
# File lib/network/client.rb, line 180 def set_basic_auth(username, password) @username = username.nil? ? '' : username @password = password.nil? ? '' : password end
Assigns authentication bearer type token for use in standard HTTP authorization header.
Parameters:¶ ↑
- token
-
string
bearer token value.
Returns:¶ ↑
- @bearer_token
-
string
the newly assigned +@bearer_token+ value.
# File lib/network/client.rb, line 194 def set_bearer_auth(token: '') @bearer_token = token end
Sets the client logger object. Execution is yielded to passed block
to set, customize, and returning a logger instance.
Returns:¶ ↑
logger
instance variable.
# File lib/network/client.rb, line 168 def set_logger @logger = if block_given? yield elsif defined?(Rails) Rails.logger else logger = Logger.new(STDOUT) logger.level = Logger::DEBUG logger end end
Assigns custom authentication token for use in standard HTTP authorization header. This takes precedence over Bearer authentication if both are set.
Parameters:¶ ↑
- header_value
-
string
full authorization header value. _(e.g. Token token=123)_.
Returns:¶ ↑
- @auth_token_header
-
string
the newly assigned +@auth_token_header+ value.
# File lib/network/client.rb, line 208 def set_token_auth(header_value: '') @auth_token_header = header_value end
Assigns a new User-Agent
header to be sent in any subsequent request.
Parameters:¶ ↑
- new_user_agent
-
string
the user-agent header value.
Returns:¶ ↑
- @user_agent
-
string
the newly assignedUser-Agent
header value.
# File lib/network/client.rb, line 221 def set_user_agent(new_user_agent) @user_agent = @default_headers['User-Agent'] = new_user_agent end
Private Instance Methods
# File lib/network/client.rb, line 290 def authenticate(headers) headers['Authorization'] = "Bearer #{bearer_token}" unless bearer_token.empty? headers['Authorization'] = auth_token_header unless auth_token_header.empty? headers end
# File lib/network/client.rb, line 286 def basic_auth(request) request.basic_auth(@username, @password) unless @username.empty? && @password.empty? end
# File lib/network/client.rb, line 237 def define_error_strategies @errors_to_recover = [Net::HTTPTooManyRequests, Net::HTTPServerError, Net::ProtocolError, Net::HTTPBadResponse, Net::ReadTimeout, Net::OpenTimeout, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::ECONNRESET, Errno::EHOSTUNREACH, Timeout::Error, OpenSSL::SSL::SSLError, EOFError, SocketError, IOError] @errors_to_propagate = [Net::HTTPRequestURITooLarge, Net::HTTPMethodNotAllowed, Zlib::BufError, OpenSSL::X509::CertificateError] end
# File lib/network/client.rb, line 332 def encode_path_params(path, params) if params.nil? || params.empty? path else params = stringify_keys(params) encoded = URI.encode_www_form(params) [path, encoded].join("?") end end
# File lib/network/client.rb, line 342 def formulate_path(path) path = '/' if path.nil? || path.empty? path = path.strip if path.respond_to?(:strip) path.prepend('/') unless path.chars.first == '/' path end
# File lib/network/client.rb, line 296 def http_request(request) tries_count ||= @tries finished = ->() { (tries_count -= 1).zero? } begin response = @http.request(request) end until !recoverable?(response) || finished.call response rescue *@errors_to_propagate => error log "Request Failed. \nReason: #{error.message}" raise rescue *@errors_to_recover => error warn_on_retry "#{error.message}" finished.call ? raise : retry end
# File lib/network/client.rb, line 349 def log(message) @logger.error("\n#{LOG_TAG} #{message}.") end
# File lib/network/client.rb, line 323 def parse_as_json(response_body) body = response_body body = body.nil? || body.empty? ? body : JSON.parse(body) rescue JSON::ParserError => error log "Parsing response body as JSON failed! Returning raw body. \nDetails: \n#{error.message}" body end
# File lib/network/client.rb, line 314 def recoverable?(response) if @errors_to_recover.any? { |error_class| response.is_a?(error_class) } warn_on_retry "#{response.class} response type." true else false end end
# File lib/network/client.rb, line 265 def request(http_method, path, params, headers) path = formulate_path(path) path = encode_path_params(path, params) if http_method == :get headers = @default_headers.merge(headers) headers = authenticate(headers) request = Net::HTTP::const_get(http_method.to_s.capitalize.to_sym).new(path, headers) request.body = params.to_s unless http_method == :get basic_auth(request) response = http_request(request) unless Net::HTTPSuccess === response log "endpoint responded with non-success #{response.code} code.\nResponse: #{response.body}" end response end
# File lib/network/client.rb, line 259 def request_json(http_method, path, params, headers) response = request(http_method, path, params, headers) body = parse_as_json(response.body) Response.new(response.code.to_i, body) end
# File lib/network/client.rb, line 233 def set_default_headers(headers) @default_headers = DEFAULT_HEADERS.merge(headers) end
# File lib/network/client.rb, line 227 def set_http_client @http = Net::HTTP.new(@uri.host, @uri.port) @http.use_ssl = @uri.scheme == 'https' ? true : false @http.verify_mode = OpenSSL::SSL::VERIFY_NONE end
# File lib/network/client.rb, line 357 def stringify_keys(params) params.respond_to?(:keys) ? params.collect { |k, v| [k.to_s, v] }.to_h : params end
# File lib/network/client.rb, line 353 def warn_on_retry(message) @logger.warn("\n#{LOG_TAG} #{message} \nRetrying now ..") end