Class: Chimps::Request

Inherits:
RestClient::Resource
  • Object
show all
Defined in:
lib/chimps/request.rb

Overview

A class to encapsulate requests made of Infochimps.

Essentialy a wrapper for RestClient::Resource with added funcionality for automatically signing requests and parsing Infochimps API responses.

Direct Known Subclasses

QueryRequest

Constant Summary collapse

DEFAULT_HEADERS =

Default headers to pass with every request.

{ :content_type => 'application/json', :accept => 'application/json', :user_agent => "Chimps #{Chimps.version}" }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, options = {}) ⇒ Chimps::Request

Initialize a Request to the given path.

Query parameters and data can be passed in as hashes named :params and :body, respectively.

If :sign is passed in the options then the URL of this request will be signed with the Chimps user’s Infochimps API key and secret. Signing a request which doesn’t need to be signed is just fine. Forgetting to sign a request which needs to be signed will result in a 401 error from Infochimps.

If :sign_if_possible is passed in the options then an attemp to sign the URL will be made though an error will not raise an error.

Parameters:

  • path (String)
  • options (Hash) (defaults to: {})
  • potions (Hash)

    a customizable set of options

Options Hash (options):

  • query (Hash)

    Query parameters to include in the URL

  • body (Hash)

    Data to include in the request body

  • sign (true, false)

    Sign this request, raising an error on failure

  • sign_if_possible (true, false)

    Sign this request, no error on failure



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/chimps/request.rb', line 50

def initialize path, options={}
  self.path         = path
  self.query_params = options[:query_params] || options[:query]  || options[:params] || {}
  self.body         = options[:body]         || options[:data]   || {}

  @authentication_required      = [:authenticate, :authenticated, :authenticate_if_possible, :sign, :signed, :sign_if_possible].any? { |key| options[key] }
  @forgive_authentication_error = options[:sign_if_possible] || options[:authenticate_if_possible]
  @raw                          = options[:raw]
  authenticate_if_necessary!
  super(url_with_query_string, {:headers => DEFAULT_HEADERS.merge(options[:headers] || {})})
end

Instance Attribute Details

#bodyObject

Data to include in the body of the request. Can be a Hash or a String.



25
26
27
# File 'lib/chimps/request.rb', line 25

def body
  @body
end

#pathObject

Path of the URL to submit to. Must be a String. Can start with an initial ‘/’ or not – no big deal ;)



17
18
19
# File 'lib/chimps/request.rb', line 17

def path
  @path
end

#query_paramsObject

Parameters to include in the query string of the URL to submit to. Can be a string or a Hash



21
22
23
# File 'lib/chimps/request.rb', line 21

def query_params
  @query_params
end

Instance Method Details

#authenticable?true, false Also known as: signable?

Is this request authentiable (has the Chimps user specified an API key and secret in their configuration file)?

Returns:

  • (true, false)


104
105
106
# File 'lib/chimps/request.rb', line 104

def authenticable?
  Chimps.config[:catalog][:key] && Chimps.config[:catalog][:secret]
end

#authenticate?true, false Also known as: sign?

Should the request be authenticated?

Returns:

  • (true, false)


77
78
79
# File 'lib/chimps/request.rb', line 77

def authenticate?
  @authentication_required
end

#authenticate_if_necessary!Object

Authenticate this request by stuffing the :requested_at and :apikey properties into its :query_params hash.

Will do nothing at all if Chimps::Request#authenticate? returns false.



204
205
206
207
208
209
# File 'lib/chimps/request.rb', line 204

def authenticate_if_necessary!
  return unless authenticate? && should_encode?
  raise Chimps::AuthenticationError.new("Catalog API key (Chimps.config[:catalog][:key]) or secret (Chimps.config[:catalog][:secret]) missing from #{Chimps.config[:config]} or #{Chimps.config[:site_config]}") unless (authenticable? || @forgive_authentication_error)
  query_params[:requested_at] = Time.now.to_i.to_s
  query_params[:apikey]       = Chimps.config[:catalog][:key]
end

#base_urlString

Return the base URL for this request, consisting of the host and path but not the query string.

Returns:



120
121
122
# File 'lib/chimps/request.rb', line 120

def base_url
  File.join(host, path)
end

#delete(options = {}, &block) ⇒ Chimps::Response

Perform a DELETE request to this URL, returning a parsed response.

Any headers in options will passed to RestClient::Resource.delete.

Parameters:

  • options (Hash) (defaults to: {})

Returns:



181
182
183
184
185
186
# File 'lib/chimps/request.rb', line 181

def delete options={}, &block
  handle_exceptions do
    Chimps.log.info("DELETE #{url}")
    Response.new(super(options, &block))
  end
end

#encode(obj) ⇒ Object



238
239
240
241
# File 'lib/chimps/request.rb', line 238

def encode obj
  require 'json'
  JSON.generate((obj == true ? {} : obj), {:max_nesting => false})
end

#encoded_bodyString

Return this Requests’s body as a suitably encoded string.

This is the text that will be signed for POST and PUT requests.

Returns:



234
235
236
# File 'lib/chimps/request.rb', line 234

def encoded_body
  @encoded_body ||= should_encode? ? encode(body) : body.to_s
end

#get(options = {}, &block) ⇒ Chimps::Response

Perform a GET request to this URL, returning a parsed response.

Any headers in options will passed to RestClient::Resource.get.

Parameters:

  • options (Hash) (defaults to: {})

Returns:



138
139
140
141
142
143
# File 'lib/chimps/request.rb', line 138

def get options={}, &block
  handle_exceptions do
    Chimps.log.info("GET #{url}")
    Response.new(super(options, &block))
  end
end

#handle_exceptions(&block) ⇒ Object

Yield to block but rescue any RestClient errors by wrapping them in a Chimps::Response.



190
191
192
193
194
195
196
# File 'lib/chimps/request.rb', line 190

def handle_exceptions &block
  begin
    yield
  rescue RestClient::Exception => e
    Response.new(e.response, :error => e.message)
  end
end

#hostString

The host to send requests to.

Returns:



112
113
114
# File 'lib/chimps/request.rb', line 112

def host
  @host ||= Chimps.config[:catalog][:host]
end

#obj_to_stripped_string(obj) ⇒ String

Turn obj into a string, sorting on internal keys.

Parameters:

Returns:



273
274
275
276
277
278
279
# File 'lib/chimps/request.rb', line 273

def obj_to_stripped_string obj
  case obj
  when Hash   then obj.keys.map(&:to_s).sort.map { |key| [key.to_s.downcase, obj_to_stripped_string(obj[key.to_sym])].join('') }.join('')
  when Array  then obj.map { |e| obj_to_stripped_string(e) }.join('')
  else             obj.to_s
  end
end

#post(options = {}, &block) ⇒ Chimps::Response

Perform a POST request to this URL, returning a parsed response.

Any headers in options will passed to RestClient::Resource.post.

Parameters:

  • options (Hash) (defaults to: {})

Returns:



152
153
154
155
156
157
# File 'lib/chimps/request.rb', line 152

def post options={}, &block
  handle_exceptions do
    Chimps.log.info("POST #{url}")
    Response.new(super(encoded_body, options, &block))
  end
end

#put(options = {}, &block) ⇒ Chimps::Response

Perform a PUT request to this URL, returning a parsed response.

Any headers in options will passed to RestClient::Resource.put.

Parameters:

  • options (Hash) (defaults to: {})

Returns:



166
167
168
169
170
171
# File 'lib/chimps/request.rb', line 166

def put options={}, &block
  handle_exceptions do
    Chimps.log.info("PUT #{url}")
    Response.new(super(encoded_body, options, &block))
  end
end

#query_stringString

Return the query string for this request, signed if necessary.

Returns:



127
128
129
# File 'lib/chimps/request.rb', line 127

def query_string
  (authenticate? && authenticable?) ? signed_query_string : unsigned_query_string
end

#raw?true, false

Should this be considered a raw request in which neither the query string nor the body should be encoded or escaped?

Returns:

  • (true, false)


96
97
98
# File 'lib/chimps/request.rb', line 96

def raw?
  !!@raw
end

#should_encode?true, false

Should the query string and request body be encoded?

Control this by passing in the :raw keyword when initializing this Request.

Returns:

  • (true, false)


88
89
90
# File 'lib/chimps/request.rb', line 88

def should_encode?
  !@raw
end

#sign(string) ⇒ String

Sign string by concatenting it with the secret and computing the MD5 digest of the whole thing.

Parameters:

Returns:

Raises:



248
249
250
251
252
# File 'lib/chimps/request.rb', line 248

def sign string
  raise Chimps::AuthenticationError.new("No Catalog API secret stored in #{Chimps.config[:config]} or #{Chimps.config[:site_config]}.  Set Chimps.config[:catalog][:secret].") unless (authenticable? || @forgive_authentication_error)
  require 'digest/md5'
  Digest::MD5.hexdigest(string + Chimps.config[:catalog][:secret])
end

#signed_query_stringString

Append the signature to the unsigned query string.

The signature made from the Chimps user’s API secret and either the query string text (stripped of & and =) for GET and DELETE requests or the request body for POST and PUT requests.

Returns:



262
263
264
265
266
267
# File 'lib/chimps/request.rb', line 262

def signed_query_string
  return unsigned_query_string unless should_encode?
  text_to_sign = ((body == true || (! body.empty?)) ? encoded_body : unsigned_query_string_stripped)
  signature    = sign(text_to_sign)
  "#{unsigned_query_string}&signature=#{signature}"
end

#unsigned_query_stringString

Return an unsigned query string for this request.

Returns:



214
215
216
# File 'lib/chimps/request.rb', line 214

def unsigned_query_string
  (should_encode? ? RestClient::Payload.generate(query_params) : query_params).to_s
end

#unsigned_query_string_strippedString

Return an unsigned query string for this request without the & and = characters.

This is the text that will be signed for GET and DELETE requests.

Returns:



225
226
227
# File 'lib/chimps/request.rb', line 225

def unsigned_query_string_stripped
  @query_params_text ||= obj_to_stripped_string(query_params)
end

#url_with_query_stringString

Return the URL for this request with the (signed, if necessary) query string appended.

Returns:



66
67
68
69
70
71
72
# File 'lib/chimps/request.rb', line 66

def url_with_query_string
  if query_string && query_string.size > 0
    base_url + "?#{query_string}"
  else
    base_url
  end
end