Class: Miasma::Types::Api
- Inherits:
-
Object
- Object
- Miasma::Types::Api
- Includes:
- Bogo::Logger::Helpers, Utils::Lazy, Utils::Memoization
- Defined in:
- lib/miasma/types/api.rb
Overview
Remote API connection
Direct Known Subclasses
Models::AutoScale, Models::Compute, Models::LoadBalancer, Models::Orchestration, Models::Storage
Constant Summary collapse
- VALID_REQUEST_RETRY_METHODS =
HTTP request methods that are allowed retry
[:get, :head]
- MAX_REQUEST_RETRIES =
Maximum allowed HTTP request retries (for non-HTTP related errors)
5
Instance Method Summary collapse
-
#after_setup(creds) ⇒ TrueClass
Simple hook for concrete APIs to make adjustments after attribute population and prior to connection.
-
#api_for(type) ⇒ Api
Build new API for specified type using current provider / creds.
-
#connect ⇒ self
Connect to the remote API.
- #connection ⇒ HTTP
-
#custom_setup(creds) ⇒ TrueClass
Simple hook for concrete APIs to make adjustments prior to initialization and connection.
-
#endpoint ⇒ String
Url endpoint.
-
#format_response(result, extract_body = true) ⇒ Smash
Makes best attempt at formatting response.
-
#from_json(string) ⇒ Hash, ...
Convert from JSON.
-
#from_xml(string) ⇒ Hash, ...
Convert from JSON.
-
#initialize(creds) ⇒ self
constructor
Create new API connection.
-
#make_request(connection, http_method, request_args) ⇒ HTTP::Response
Perform request.
-
#perform_request_retry(exception) ⇒ TrueClass, FalseClass
Determine if a retry on the request should be performed.
-
#provider ⇒ Symbol
Name of provider.
-
#request(args) ⇒ Smash
Perform request to remote API.
-
#retryable_allowed?(http_method) ⇒ TrueClass, FalseClass
Determine if request type is allowed to be retried.
-
#retryable_request(http_method) { ... } ⇒ Object
If HTTP request method is allowed to be retried then retry request on non-response failures.
Constructor Details
#initialize(creds) ⇒ self
Create new API connection
25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/miasma/types/api.rb', line 25 def initialize(creds) custom_setup(creds) if creds.is_a?(Hash) load_data(creds) else raise TypeError, "Expecting `credentials` to be of type `Hash`. " \ "Received: `#{creds.class}`" end after_setup(creds) connect end |
Instance Method Details
#after_setup(creds) ⇒ TrueClass
Simple hook for concrete APIs to make adjustments after attribute population and prior to connection
51 52 53 |
# File 'lib/miasma/types/api.rb', line 51 def after_setup(creds) true end |
#api_for(type) ⇒ Api
Build new API for specified type using current provider / creds
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/miasma/types/api.rb', line 71 def api_for(type) memoize(type) do Miasma.api( Smash.new( :type => type, :provider => provider, :credentials => attributes, ) ) end end |
#connect ⇒ self
Connect to the remote API
63 64 65 |
# File 'lib/miasma/types/api.rb', line 63 def connect self end |
#connection ⇒ HTTP
84 85 86 |
# File 'lib/miasma/types/api.rb', line 84 def connection HTTP.headers("User-Agent" => "miasma/v#{Miasma::VERSION}") end |
#custom_setup(creds) ⇒ TrueClass
Simple hook for concrete APIs to make adjustments prior to initialization and connection
42 43 44 |
# File 'lib/miasma/types/api.rb', line 42 def custom_setup(creds) true end |
#endpoint ⇒ String
Returns url endpoint.
89 90 91 |
# File 'lib/miasma/types/api.rb', line 89 def endpoint "http://api.example.com" end |
#format_response(result, extract_body = true) ⇒ Smash
Makes best attempt at formatting response
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/miasma/types/api.rb', line 188 def format_response(result, extract_body = true) extracted_headers = Smash[result.headers.map { |k, v| [Utils.snake(k), v] }] if extract_body logger.debug("extracting HTTP response body") body_content = result.body.to_s body_content.encode!("UTF-8", "binary", :invalid => :replace, :undef => :replace, :replace => "") if extracted_headers[:content_type].to_s.include?("json") logger.debug("unpacking HTTP response body using JSON") extracted_body = from_json(body_content) || body_content elsif extracted_headers[:content_type].to_s.include?("xml") logger.debug("unpacking HTTP response body using XML") extracted_body = from_xml(body_content) || body_content else logger.debug("unpacking HTTP response body using best effort") extracted_body = from_json(body_content) || from_xml(body_content) || body_content end end unless extracted_body # @note if body is over 100KB, do not extract if extracted_headers[:content_length].to_i < 102400 logger.warn("skipping body extraction and formatting due to size") extracted_body = result.body.to_s else extracted_body = result.body end end Smash.new( :response => result, :headers => extracted_headers, :body => extracted_body, ) end |
#from_json(string) ⇒ Hash, ...
Convert from JSON
230 231 232 233 234 235 236 237 238 239 |
# File 'lib/miasma/types/api.rb', line 230 def from_json(string) begin MultiJson.load(string).to_smash rescue MultiJson::ParseError => err logger.error("failed to load JSON string - #{err.class}: #{err}") logger.debug("JSON string load failure: #{string.inspect}") logger.debug("JSON error - #{err.class}: #{err}\n#{err.backtrace.join("\n")}") nil end end |
#from_xml(string) ⇒ Hash, ...
Convert from JSON
245 246 247 248 249 250 251 252 253 254 |
# File 'lib/miasma/types/api.rb', line 245 def from_xml(string) begin MultiXml.parse(string).to_smash rescue MultiXml::ParseError => err logger.error("failed to load XML string - #{err.class}: #{err}") logger.debug("XML string load failure: #{string.inspect}") logger.debug("XML error - #{err.class}: #{err}\n#{err.backtrace.join("\n")}") nil end end |
#make_request(connection, http_method, request_args) ⇒ HTTP::Response
this is mainly here for concrete APIs to override if things need to be done prior to the actual request (like signature generation)
Perform request
178 179 180 181 |
# File 'lib/miasma/types/api.rb', line 178 def make_request(connection, http_method, request_args) logger.debug("making #{http_method.to_s.upcase} request - #{request_args.inspect}") connection.send(http_method, *request_args) end |
#perform_request_retry(exception) ⇒ TrueClass, FalseClass
Determine if a retry on the request should be performed
165 166 167 |
# File 'lib/miasma/types/api.rb', line 165 def perform_request_retry(exception) true end |
#provider ⇒ Symbol
Returns name of provider.
56 57 58 |
# File 'lib/miasma/types/api.rb', line 56 def provider Utils.snake(self.class.to_s.split("::").last).to_sym end |
#request(args) ⇒ Smash
Perform request to remote API
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 |
# File 'lib/miasma/types/api.rb', line 102 def request(args) args = args.to_smash http_method = args.fetch(:method, "get").to_s.downcase.to_sym unless HTTP::Request::METHODS.include?(http_method) raise ArgumentError.new "Invalid request method provided!" end request_args = [].tap do |ary| _endpoint = args.delete(:endpoint) || endpoint ary.push(File.join(_endpoint, args[:path].to_s)) = {}.tap do |opts| [:form, :params, :json, :body].each do |key| opts[key] = args[key] if args[key] end end ary.push() unless .empty? end if args[:headers] _connection = connection.headers(args[:headers]) args.delete(:headers) else _connection = connection end result = retryable_request(http_method) do res = make_request(_connection, http_method, request_args) unless [args.fetch(:expects, 200)].flatten.compact.map(&:to_i).include?(res.code) raise Error::ApiError::RequestError.new(res.reason, :response => res) end res end format_response(result, !args[:disable_body_extraction]) end |
#retryable_allowed?(http_method) ⇒ TrueClass, FalseClass
Determine if request type is allowed to be retried
157 158 159 |
# File 'lib/miasma/types/api.rb', line 157 def retryable_allowed?(http_method) VALID_REQUEST_RETRY_METHODS.include?(http_method) end |
#retryable_request(http_method) { ... } ⇒ Object
If HTTP request method is allowed to be retried then retry request on non-response failures. Otherwise just re-raise immediately
141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/miasma/types/api.rb', line 141 def retryable_request(http_method, &block) Bogo::Retry.build( data.fetch(:retry_type, :exponential), :max_attempts => retryable_allowed?(http_method) ? data.fetch(:retry_max, MAX_REQUEST_RETRIES) : 0, :wait_interval => data[:retry_interval], :ui => data[:retry_ui], :auto_run => false, &block ).run! { |e| perform_request_retry(e) } end |