1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
|
require 'faraday'
require 'faraday_middleware'
require 'faraday_middleware/multi_json'
require_relative 'http_client/error_handling'
require_relative 'http_client/environment_info'
require_relative 'http_client/response'
module Asana
# Internal: Wrapper over Faraday that abstracts authentication, request
# parsing and common options.
class HttpClient
# Internal: The API base URI.
BASE_URI = 'https://app.asana.com/api/1.0'
# Public: Initializes an HttpClient to make requests to the Asana API.
#
# authentication - [Asana::Authentication] An authentication strategy.
# adapter - [Symbol, Proc] A Faraday adapter, eiter a Symbol for
# registered adapters or a Proc taking a builder for a
# custom one. Defaults to Faraday.default_adapter.
# user_agent - [String] The user agent. Defaults to "ruby-asana vX.Y.Z".
# config - [Proc] An optional block that yields the Faraday builder
# object for customization.
def initialize(authentication: required('authentication'),
adapter: nil,
user_agent: nil,
debug_mode: false,
&config)
@authentication = authentication
@adapter = adapter || Faraday.default_adapter
@environment_info = EnvironmentInfo.new(user_agent)
@debug_mode = debug_mode
@config = config
end
# Public: Performs a GET request against the API.
#
# resource_uri - [String] the resource URI relative to the base Asana API
# URL, e.g "/users/me".
# params - [Hash] the request parameters
# options - [Hash] the request I/O options
#
# Returns an [Asana::HttpClient::Response] if everything went well.
# Raises [Asana::Errors::APIError] if anything went wrong.
def get(resource_uri, params: {}, options: {})
opts = options.reduce({}) do |acc, (k, v)|
acc.tap do |hash|
hash[:"opt_#{k}"] = v.is_a?(Array) ? v.join(',') : v
end
end
perform_request(:get, resource_uri, params.merge(opts))
end
# Public: Performs a PUT request against the API.
#
# resource_uri - [String] the resource URI relative to the base Asana API
# URL, e.g "/users/me".
# body - [Hash] the body to PUT.
# options - [Hash] the request I/O options
#
# Returns an [Asana::HttpClient::Response] if everything went well.
# Raises [Asana::Errors::APIError] if anything went wrong.
def put(resource_uri, body: {}, options: {})
params = { data: body }.merge(options.empty? ? {} : { options: options })
perform_request(:put, resource_uri, params)
end
# Public: Performs a POST request against the API.
#
# resource_uri - [String] the resource URI relative to the base Asana API
# URL, e.g "/tags".
# body - [Hash] the body to POST.
# upload - [Faraday::UploadIO] an upload object to post as multipart.
# Defaults to nil.
# options - [Hash] the request I/O options
#
# Returns an [Asana::HttpClient::Response] if everything went well.
# Raises [Asana::Errors::APIError] if anything went wrong.
def post(resource_uri, body: {}, upload: nil, options: {})
params = { data: body }.merge(options.empty? ? {} : { options: options })
if upload
perform_request(:post, resource_uri, params.merge(file: upload)) do |c|
c.request :multipart
end
else
perform_request(:post, resource_uri, params)
end
end
# Public: Performs a DELETE request against the API.
#
# resource_uri - [String] the resource URI relative to the base Asana API
# URL, e.g "/tags".
#
# Returns an [Asana::HttpClient::Response] if everything went well.
# Raises [Asana::Errors::APIError] if anything went wrong.
def delete(resource_uri)
perform_request(:delete, resource_uri)
end
private
def connection(&request_config)
Faraday.new do |builder|
@authentication.configure(builder)
@environment_info.configure(builder)
request_config.call(builder) if request_config
configure_format(builder)
add_middleware(builder)
@config.call(builder) if @config
use_adapter(builder, @adapter)
end
end
def perform_request(method, resource_uri, body = {}, &request_config)
handling_errors do
url = BASE_URI + resource_uri
log_request(method, url, body) if @debug_mode
Response.new(connection(&request_config).public_send(method, url, body))
end
end
def configure_format(builder)
builder.request :multi_json
builder.response :multi_json
end
def add_middleware(builder)
builder.use Faraday::Response::RaiseError
builder.use FaradayMiddleware::FollowRedirects
end
def use_adapter(builder, adapter)
case adapter
when Symbol
builder.adapter(adapter)
when Proc
adapter.call(builder)
end
end
def handling_errors(&request)
ErrorHandling.handle(&request)
end
def log_request(method, url, body)
STDERR.puts format('[%s] %s %s (%s)',
self.class,
method.to_s.upcase,
url,
body.inspect)
end
end
end
|