Skip to content

[Ruby] Using partials in api_client to better support both Faraday and Typhoeus #3564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,12 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("travis.mustache", "", ".travis.yml"));
supportingFiles.add(new SupportingFile("gemspec.mustache", "", gemName + ".gemspec"));
supportingFiles.add(new SupportingFile("configuration.mustache", gemFolder, "configuration.rb"));
supportingFiles.add(new SupportingFile("api_client.mustache", gemFolder, "api_client.rb"));

if (TYPHOEUS.equals(getLibrary())) {
supportingFiles.add(new SupportingFile("api_client.mustache", gemFolder, "api_client.rb"));
// for Typhoeus
} else if (FARADAY.equals(getLibrary())) {
supportingFiles.add(new SupportingFile("faraday_api_client.mustache", gemFolder, "api_client.rb"));
// for Faraday
additionalProperties.put("isFaraday", Boolean.TRUE);
} else {
throw new RuntimeException("Invalid HTTP library " + getLibrary() + ". Only faraday, typhoeus are supported.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ require 'date'
require 'json'
require 'logger'
require 'tempfile'
{{^isFaraday}}
require 'typhoeus'
{{/isFaraday}}
{{#isFaraday}}
require 'faraday'
{{/isFaraday}}

module {{moduleName}}
class ApiClient
Expand All @@ -33,94 +38,12 @@ module {{moduleName}}
@@default ||= ApiClient.new
end

# Call an API with given options.
#
# @return [Array<(Object, Integer, Hash)>] an array of 3 elements:
# the data deserialized from response body (could be nil), response status code and response headers.
def call_api(http_method, path, opts = {})
request = build_request(http_method, path, opts)
response = request.run

if @config.debugging
@config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
end

unless response.success?
if response.timed_out?
fail ApiError.new('Connection timed out')
elsif response.code == 0
# Errors from libcurl will be made visible here
fail ApiError.new(:code => 0,
:message => response.return_message)
else
fail ApiError.new(:code => response.code,
:response_headers => response.headers,
:response_body => response.body),
response.status_message
end
end

if opts[:return_type]
data = deserialize(response, opts[:return_type])
else
data = nil
end
return data, response.code, response.headers
end

# Builds the HTTP request
#
# @param [String] http_method HTTP method/verb (e.g. POST)
# @param [String] path URL path (e.g. /account/new)
# @option opts [Hash] :header_params Header parameters
# @option opts [Hash] :query_params Query parameters
# @option opts [Hash] :form_params Query parameters
# @option opts [Object] :body HTTP body (JSON/XML)
# @return [Typhoeus::Request] A Typhoeus Request
def build_request(http_method, path, opts = {})
url = build_request_url(path)
http_method = http_method.to_sym.downcase

header_params = @default_headers.merge(opts[:header_params] || {})
query_params = opts[:query_params] || {}
form_params = opts[:form_params] || {}

{{#hasAuthMethods}}
update_params_for_auth! header_params, query_params, opts[:auth_names]
{{/hasAuthMethods}}

# set ssl_verifyhosts option based on @config.verify_ssl_host (true/false)
_verify_ssl_host = @config.verify_ssl_host ? 2 : 0

req_opts = {
:method => http_method,
:headers => header_params,
:params => query_params,
:params_encoding => @config.params_encoding,
:timeout => @config.timeout,
:ssl_verifypeer => @config.verify_ssl,
:ssl_verifyhost => _verify_ssl_host,
:sslcert => @config.cert_file,
:sslkey => @config.key_file,
:verbose => @config.debugging
}

# set custom cert, if provided
req_opts[:cainfo] = @config.ssl_ca_cert if @config.ssl_ca_cert

if [:post, :patch, :put, :delete].include?(http_method)
req_body = build_request_body(header_params, form_params, opts[:body])
req_opts.update :body => req_body
if @config.debugging
@config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
end
end

request = Typhoeus::Request.new(url, req_opts)
download_file(request) if opts[:return_type] == 'File'
request
end

{{^isFaraday}}
{{> api_client_typhoeus_partial}}
{{/isFaraday}}
{{#isFaraday}}
{{> api_client_faraday_partial}}
{{/isFaraday}}
# Check if the given MIME is a JSON MIME.
# JSON MIME examples:
# application/json
Expand Down Expand Up @@ -258,34 +181,6 @@ module {{moduleName}}
@config.base_url + path
end

# Builds the HTTP request body
#
# @param [Hash] header_params Header parameters
# @param [Hash] form_params Query parameters
# @param [Object] body HTTP body (JSON/XML)
# @return [String] HTTP body data in the form of string
def build_request_body(header_params, form_params, body)
# http form
if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
header_params['Content-Type'] == 'multipart/form-data'
data = {}
form_params.each do |key, value|
case value
when ::File, ::Array, nil
# let typhoeus handle File, Array and nil parameters
data[key] = value
else
data[key] = value.to_s
end
end
elsif body
data = body.is_a?(String) ? body : body.to_json
else
data = nil
end
data
end

# Update hearder and query params based on authentication settings.
#
# @param [Hash] header_params Header parameters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Call an API with given options.
#
# @return [Array<(Object, Integer, Hash)>] an array of 3 elements:
# the data deserialized from response body (could be nil), response status code and response headers.
def call_api(http_method, path, opts = {})
ssl_options = {
:ca_file => @config.ssl_ca_file,
:verify => @config.ssl_verify,
:verify => @config.ssl_verify_mode,
:client_cert => @config.ssl_client_cert,
:client_key => @config.ssl_client_key
}

connection = Faraday.new(:url => config.base_url, :ssl => ssl_options) do |conn|
conn.basic_auth(config.username, config.password)
if opts[:header_params]["Content-Type"] == "multipart/form-data"
conn.request :multipart
conn.request :url_encoded
end
conn.adapter(Faraday.default_adapter)
end

begin
response = connection.public_send(http_method.to_sym.downcase) do |req|
build_request(http_method, path, req, opts)
end

if @config.debugging
@config.logger.debug "HTTP response body ~BEGIN~\n#{response.body}\n~END~\n"
end

unless response.success?
if response.status == 0
# Errors from libcurl will be made visible here
fail ApiError.new(:code => 0,
:message => response.return_message)
else
fail ApiError.new(:code => response.status,
:response_headers => response.headers,
:response_body => response.body),
response.reason_phrase
end
end
rescue Faraday::TimeoutError
fail ApiError.new('Connection timed out')
end

if opts[:return_type]
data = deserialize(response, opts[:return_type])
else
data = nil
end
return data, response.status, response.headers
end

# Builds the HTTP request
#
# @param [String] http_method HTTP method/verb (e.g. POST)
# @param [String] path URL path (e.g. /account/new)
# @option opts [Hash] :header_params Header parameters
# @option opts [Hash] :query_params Query parameters
# @option opts [Hash] :form_params Query parameters
# @option opts [Object] :body HTTP body (JSON/XML)
# @return [Typhoeus::Request] A Typhoeus Request
def build_request(http_method, path, request, opts = {})
url = build_request_url(path)
http_method = http_method.to_sym.downcase

header_params = @default_headers.merge(opts[:header_params] || {})
query_params = opts[:query_params] || {}
form_params = opts[:form_params] || {}

update_params_for_auth! header_params, query_params, opts[:auth_names]

req_opts = {
:method => http_method,
:headers => header_params,
:params => query_params,
:params_encoding => @config.params_encoding,
:timeout => @config.timeout,
:verbose => @config.debugging
}

if [:post, :patch, :put, :delete].include?(http_method)
req_body = build_request_body(header_params, form_params, opts[:body])
req_opts.update :body => req_body
if @config.debugging
@config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
end
end
request.headers = header_params
request.body = req_body
request.url url
request.params = query_params
download_file(request) if opts[:return_type] == 'File'
request
end

# Builds the HTTP request body
#
# @param [Hash] header_params Header parameters
# @param [Hash] form_params Query parameters
# @param [Object] body HTTP body (JSON/XML)
# @return [String] HTTP body data in the form of string
def build_request_body(header_params, form_params, body)
# http form
if header_params['Content-Type'] == 'application/x-www-form-urlencoded'
data = URI.encode_www_form(form_params)
elsif header_params['Content-Type'] == 'multipart/form-data'
data = {}
form_params.each do |key, value|
case value
when ::File, ::Tempfile
# TODO hardcode to application/octet-stream, need better way to detect content type
data[key] = Faraday::UploadIO.new(value.path, 'application/octet-stream', value.path)
when ::Array, nil
# let Faraday handle Array and nil parameters
data[key] = value
else
data[key] = value.to_s
end
end
elsif body
data = body.is_a?(String) ? body : body.to_json
else
data = nil
end
data
end
Loading