vacuum 0.1.3 → 0.2.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +69 -27
- data/lib/vacuum/endpoint/base.rb +58 -0
- data/lib/vacuum/endpoint/mws.rb +59 -0
- data/lib/vacuum/endpoint/product_advertising.rb +34 -0
- data/lib/vacuum/mws.rb +8 -0
- data/lib/vacuum/product_advertising.rb +7 -0
- data/lib/vacuum/request/base.rb +96 -0
- data/lib/vacuum/request/mws.rb +24 -0
- data/lib/vacuum/request/product_advertising.rb +90 -0
- data/lib/vacuum/request/signature/authentication.rb +32 -0
- data/lib/vacuum/request/signature/builder.rb +70 -0
- data/lib/vacuum/request/utils.rb +24 -0
- data/lib/vacuum/response/base.rb +57 -0
- data/lib/vacuum/response/mws.rb +7 -0
- data/lib/vacuum/response/product_advertising.rb +19 -0
- data/lib/vacuum/response/utils.rb +48 -0
- data/lib/vacuum/version.rb +1 -1
- data/lib/vacuum.rb +31 -11
- data/spec/fixtures/{http_response → product_advertising} +0 -0
- data/spec/spec_helper.rb +5 -4
- data/spec/support/shared_examples/endpoint.rb +43 -0
- data/spec/support/shared_examples/request.rb +95 -0
- data/spec/support/shared_examples/response.rb +51 -0
- data/spec/vacuum/endpoint/base_spec.rb +19 -0
- data/spec/vacuum/endpoint/mws_spec.rb +47 -0
- data/spec/vacuum/endpoint/product_advertising_spec.rb +25 -0
- data/spec/vacuum/request/base_spec.rb +22 -0
- data/spec/vacuum/request/mws_spec.rb +26 -0
- data/spec/vacuum/request/product_advertising_spec.rb +104 -0
- data/spec/vacuum/request/signature/authentication_spec.rb +30 -0
- data/spec/vacuum/request/signature/builder_spec.rb +76 -0
- data/spec/vacuum/request/utils_spec.rb +23 -0
- data/spec/vacuum/response/base_spec.rb +38 -0
- data/spec/vacuum/response/product_advertising_spec.rb +57 -0
- data/spec/vacuum/response/utils_spec.rb +42 -0
- metadata +107 -21
- data/lib/vacuum/em.rb +0 -21
- data/lib/vacuum/request.rb +0 -119
- data/lib/vacuum/response.rb +0 -63
- data/spec/vacuum/request_spec.rb +0 -130
- data/spec/vacuum/response_spec.rb +0 -80
data/README.md
CHANGED
@@ -2,42 +2,84 @@
|
|
2
2
|
|
3
3
|
[![travis] [1]] [2]
|
4
4
|
|
5
|
-
|
5
|
+
Vacuum is a Ruby wrapper to [various Amazon Web Services (AWS) APIs] [3].
|
6
6
|
|
7
|
-
|
7
|
+
![vacuum] [4]
|
8
8
|
|
9
|
-
|
9
|
+
## Amazon Product Advertising API
|
10
|
+
|
11
|
+
Vacuum knows the [Amazon Product Advertising API] [5] [inside out] [6].
|
10
12
|
|
11
13
|
```ruby
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
14
|
+
request = Vacuum.new(:product_advertising) do |config|
|
15
|
+
config.locale 'US'
|
16
|
+
|
17
|
+
config.key 'key'
|
18
|
+
config.secret 'secret'
|
19
|
+
config.tag 'tag'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Use an alternative Faraday adapter.
|
23
|
+
# request.connection do |builder|
|
24
|
+
# builder.adapter :typhoeus
|
25
|
+
# end
|
26
|
+
|
27
|
+
# A barebone search request.
|
28
|
+
request.build operation: 'ItemSearch',
|
29
|
+
search_index: 'Books',
|
30
|
+
keywords: 'Deleuze'
|
31
|
+
response = request.get
|
32
|
+
|
33
|
+
# A less verbose search.
|
34
|
+
request.search :books, 'Deleuze'
|
35
|
+
|
36
|
+
if response.valid?
|
37
|
+
# response.code
|
38
|
+
# response.body
|
39
|
+
# response.errors
|
40
|
+
# response.xml # The Nokogiri XML doc
|
41
|
+
# response.to_hash
|
42
|
+
response.find('Item') do |item|
|
32
43
|
p item['ASIN']
|
33
44
|
end
|
34
45
|
end
|
35
46
|
```
|
47
|
+
## Amazon Marketplace Web Services API
|
48
|
+
|
49
|
+
The wrapper to the [Amazon Marketplace Web Services API] [7] is a
|
50
|
+
work-in-progress.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
request = Vacuum.new(:mws_products) do |config|
|
54
|
+
config.locale 'US'
|
55
|
+
|
56
|
+
config.key 'key'
|
57
|
+
config.secret 'secret'
|
58
|
+
config.marketplace 'marketplace'
|
59
|
+
config.seller 'seller'
|
60
|
+
end
|
61
|
+
|
62
|
+
request.build 'Action' => 'GetLowestOfferListingsForASIN',
|
63
|
+
'ASINList.ASIN.1' => '0231081596'
|
64
|
+
offers = request.get.find 'GetLowestOfferListingsForASINResult'
|
65
|
+
```
|
66
|
+
|
67
|
+
## Other AWS APIs
|
68
|
+
|
69
|
+
Vacuum should work with EC2, S3, IAM, SimpleDB, SQS, SNS, SES, ELB, CW, and so
|
70
|
+
on. Implement and send a pull request.
|
71
|
+
|
72
|
+
# Addendum
|
73
|
+
|
74
|
+
![vacuums] [8]
|
36
75
|
|
37
|
-
|
76
|
+
> Workers queuing to crawl AWS.
|
38
77
|
|
39
78
|
[1]: https://secure.travis-ci.org/hakanensari/vacuum.png
|
40
79
|
[2]: http://travis-ci.org/hakanensari/vacuum
|
41
|
-
[3]: http://
|
42
|
-
[4]:
|
43
|
-
[5]: https://
|
80
|
+
[3]: http://aws.amazon.com/
|
81
|
+
[4]: http://f.cl.ly/items/2k2X0e2u0G3k1c260D2u/vacuum.png
|
82
|
+
[5]: https://affiliate-program.amazon.co.uk/gp/advertising/api/detail/main.html
|
83
|
+
[6]: https://github.com/hakanensari/vacuum/blob/master/examples/product_advertising/
|
84
|
+
[7]: https://developer.amazonservices.com/gp/mws/docs.html
|
85
|
+
[8]: http://f.cl.ly/items/1Q3W372A0H3M0w2H1e0W/hoover.jpeg
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Endpoint
|
3
|
+
# An Amazon Web Services (AWS) API endpoint.
|
4
|
+
class Base
|
5
|
+
LOCALES = %w(CA CN DE ES FR IT JP UK US)
|
6
|
+
|
7
|
+
# Raises a Not Implemented Error.
|
8
|
+
#
|
9
|
+
# When implemented, this should return a String AWS API host.
|
10
|
+
def host
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns the String AWS access key ID.
|
15
|
+
#
|
16
|
+
# Raises a Missing Key error if key is missing.
|
17
|
+
def key
|
18
|
+
@key or raise MissingKey
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets the String AWS access key ID.
|
22
|
+
attr_writer :key
|
23
|
+
|
24
|
+
# Returns the String AWS API locale (default: US).
|
25
|
+
#
|
26
|
+
# Raises a Bad Locale error if locale is not valid.
|
27
|
+
def locale
|
28
|
+
@locale ||= 'US'
|
29
|
+
LOCALES.include? @locale or raise BadLocale
|
30
|
+
|
31
|
+
@locale
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sets the String AWS API locale.
|
35
|
+
attr_writer :locale
|
36
|
+
|
37
|
+
# Returns the String AWS access secret key.
|
38
|
+
#
|
39
|
+
# Raises a Missing Secret error if secret is missing.
|
40
|
+
def secret
|
41
|
+
@secret or raise MissingSecret
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets the String AWS access secret key.
|
45
|
+
attr_writer :secret
|
46
|
+
|
47
|
+
# Returns a String user agent for the AWS API request.
|
48
|
+
def user_agent
|
49
|
+
engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
|
50
|
+
language = [engine, RUBY_VERSION, "p#{RUBY_PATCHLEVEL}"].join ' '
|
51
|
+
hostname = `hostname`.chomp
|
52
|
+
version = Vacuum::VERSION
|
53
|
+
|
54
|
+
"Vacuum/#{version} (Language=#{language}; Host=#{hostname})"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Endpoint
|
3
|
+
# A Marketplace Web Services (MWS) API endpoint.
|
4
|
+
class MWS < Base
|
5
|
+
# A list of MWS API hosts.
|
6
|
+
HOSTS = {
|
7
|
+
'CA' => 'mws.amazonservices.ca',
|
8
|
+
'CN' => 'mws.amazonservices.com.cn',
|
9
|
+
'DE' => 'mws-eu.amazonservices.com',
|
10
|
+
'ES' => 'mws-eu.amazonservices.com',
|
11
|
+
'FR' => 'mws-eu.amazonservices.com',
|
12
|
+
'IT' => 'mws-eu.amazonservices.com',
|
13
|
+
'JP' => 'mws.amazonservices.jp',
|
14
|
+
'UK' => 'mws-eu.amazonservices.com',
|
15
|
+
'US' => 'mws.amazonservices.com'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Internal: Gets/Sets the Symbol MWS API type.
|
19
|
+
attr_accessor :api
|
20
|
+
|
21
|
+
# Returns a String MWS API host.
|
22
|
+
def host
|
23
|
+
HOSTS[locale]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets the String marketplace ID.
|
27
|
+
#
|
28
|
+
# Raises a Missing Marketplace error if marketplace ID is missing.
|
29
|
+
def marketplace
|
30
|
+
@marketplace or raise MissingMarketplace
|
31
|
+
end
|
32
|
+
|
33
|
+
# Sets the String marketplace ID tag.
|
34
|
+
attr_writer :marketplace
|
35
|
+
|
36
|
+
# Returns a String MWS API URL path.
|
37
|
+
#
|
38
|
+
# Raises a Not Implemented Error if API is not implemented.
|
39
|
+
def path
|
40
|
+
case api
|
41
|
+
when :products
|
42
|
+
'/Products/2011-10-01'
|
43
|
+
else
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sets the String seller ID.
|
49
|
+
#
|
50
|
+
# Raises a Missing Seller error if seller ID is missing.
|
51
|
+
def seller
|
52
|
+
@seller or raise MissingSeller
|
53
|
+
end
|
54
|
+
|
55
|
+
# Sets the String seller ID tag.
|
56
|
+
attr_writer :seller
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Endpoint
|
3
|
+
# A Product Advertising API endpoint.
|
4
|
+
class ProductAdvertising < Base
|
5
|
+
# A list of Product Advertising API hosts.
|
6
|
+
HOSTS = {
|
7
|
+
'CA' => 'ecs.amazonaws.ca',
|
8
|
+
'CN' => 'webservices.amazon.cn',
|
9
|
+
'DE' => 'ecs.amazonaws.de',
|
10
|
+
'ES' => 'webservices.amazon.es',
|
11
|
+
'FR' => 'ecs.amazonaws.fr',
|
12
|
+
'IT' => 'webservices.amazon.it',
|
13
|
+
'JP' => 'ecs.amazonaws.jp',
|
14
|
+
'UK' => 'ecs.amazonaws.co.uk',
|
15
|
+
'US' => 'ecs.amazonaws.com'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Returns a String Product Advertising API host.
|
19
|
+
def host
|
20
|
+
HOSTS[locale]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the String Associate tag.
|
24
|
+
#
|
25
|
+
# Raises a Missing Tag error if tag is missing.
|
26
|
+
def tag
|
27
|
+
@tag or raise MissingTag
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the String Associate tag.
|
31
|
+
attr_writer :tag
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/vacuum/mws.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
# An Amazon Web Services (AWS) API request.
|
4
|
+
class Base
|
5
|
+
# Returns the AWS API Endpoint.
|
6
|
+
attr :endpoint
|
7
|
+
|
8
|
+
# Creates a new request for given locale and credentials.
|
9
|
+
#
|
10
|
+
# Yields the AWS API endpoint if a block is given.
|
11
|
+
def initialize(&blk)
|
12
|
+
@parameters = {}
|
13
|
+
@endpoint = Endpoint.const_get(class_basename).new
|
14
|
+
|
15
|
+
configure &blk if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
# Adds given parameters to the request.
|
19
|
+
#
|
20
|
+
# hsh - A Hash of parameter key and value pairs.
|
21
|
+
#
|
22
|
+
# Returns self.
|
23
|
+
def build(hsh)
|
24
|
+
hsh.each do |k, v|
|
25
|
+
k = Utils.camelize k.to_s if k.is_a? Symbol
|
26
|
+
@parameters[k] = v.is_a?(Array) ? v.join(',') : v.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
# Resets the request to the given parameters.
|
33
|
+
#
|
34
|
+
# hsh - A Hash of parameter key and value pairs.
|
35
|
+
#
|
36
|
+
# Returns self.
|
37
|
+
def build!(hsh = {})
|
38
|
+
@parameters = {}
|
39
|
+
build hsh
|
40
|
+
end
|
41
|
+
|
42
|
+
# Yields the AWS API Endpoint.
|
43
|
+
#
|
44
|
+
# Returns nothing.
|
45
|
+
def configure(&blk)
|
46
|
+
yield @endpoint
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns a Faraday::Connection.
|
50
|
+
#
|
51
|
+
# Yields a Faraday::Builder to configure the connection if a block is
|
52
|
+
# given.
|
53
|
+
def connection
|
54
|
+
@connection ||= Faraday.new do |builder|
|
55
|
+
builder.use Signature::Authentication, endpoint.secret
|
56
|
+
|
57
|
+
yield builder if block_given?
|
58
|
+
|
59
|
+
unless builder.handlers.any? { |h| h.name.include? ':Adapter:' }
|
60
|
+
builder.adapter Faraday.default_adapter
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Performs the AWS API request.
|
66
|
+
#
|
67
|
+
# Returns a Vacuum::Response::Base or a subclass thereof.
|
68
|
+
def get
|
69
|
+
res = connection.get url
|
70
|
+
Response.const_get(class_basename).new res.body, res.status
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the Hash parameters of the AWS API request.
|
74
|
+
def parameters
|
75
|
+
default_parameters.merge @parameters
|
76
|
+
end
|
77
|
+
|
78
|
+
# Raises a Not Implemented Error.
|
79
|
+
#
|
80
|
+
# When implemented, this should return an Addressable::URI.
|
81
|
+
def url
|
82
|
+
raise NotImplementedError
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def class_basename
|
88
|
+
self.class.name.split('::').last
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_parameters
|
92
|
+
{ 'AWSAccessKeyId' => endpoint.key }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
# A Marketplace Web Services (MWS) API request.
|
4
|
+
class MWS < Base
|
5
|
+
# Returns the Addressable::URI URL of the MWS API request.
|
6
|
+
def url
|
7
|
+
Addressable::URI.new :scheme => 'https',
|
8
|
+
:host => endpoint.host,
|
9
|
+
:path => endpoint.path,
|
10
|
+
:query_values => parameters
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def default_parameters
|
16
|
+
super.merge 'MarketplaceId' => endpoint.marketplace,
|
17
|
+
'SellerId' => endpoint.seller,
|
18
|
+
'Service' => 'AWSECommerceService',
|
19
|
+
'SignatureMethod' => 'HmacSHA256',
|
20
|
+
'SignatureVersion' => 2
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
# A Product Advertising API request.
|
4
|
+
class ProductAdvertising < Base
|
5
|
+
# Looks up attributes of up to twenty items.
|
6
|
+
#
|
7
|
+
# item_ids - Splat Array of item IDs. The last element may optionally
|
8
|
+
# specify a Hash of parameter key and value pairs.
|
9
|
+
#
|
10
|
+
# Examples
|
11
|
+
#
|
12
|
+
# request.find '0679753354', response_group: 'Images'
|
13
|
+
#
|
14
|
+
# Returns a Vacuum::Response.
|
15
|
+
def find(*item_ids)
|
16
|
+
given_params = item_ids.last.is_a?(Hash) ? item_ids.pop : {}
|
17
|
+
|
18
|
+
params =
|
19
|
+
case item_ids.size
|
20
|
+
when 1..10
|
21
|
+
{
|
22
|
+
'Operation' => 'ItemLookup',
|
23
|
+
'ItemId' => item_ids
|
24
|
+
}.merge given_params
|
25
|
+
when 11..20
|
26
|
+
default = {
|
27
|
+
'Operation' => 'ItemLookup',
|
28
|
+
'ItemId.1.ItemId' => item_ids.shift(10),
|
29
|
+
'ItemId.2.ItemId' => item_ids
|
30
|
+
}
|
31
|
+
given_params.reduce(default) do |a, (k, v)|
|
32
|
+
a.merge "ItemLookup.Shared.#{Utils.camelize k.to_s}" => v
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Can't look up #{item_ids.size} items"
|
36
|
+
end
|
37
|
+
build! params
|
38
|
+
|
39
|
+
get
|
40
|
+
end
|
41
|
+
|
42
|
+
# Searches for items that satisfy the given criteria, including one or
|
43
|
+
# more search indices.
|
44
|
+
#
|
45
|
+
# index - Symbol search index
|
46
|
+
# query_or_params - String keyword query or Hash of parameter key and
|
47
|
+
# value pairs (default: nil).
|
48
|
+
#
|
49
|
+
# Examples
|
50
|
+
#
|
51
|
+
# # Search the entire Amazon catalog for Deleuze.
|
52
|
+
# request.search :all, 'Deleuze'
|
53
|
+
#
|
54
|
+
# # Search books for non-fiction titles authored by Deleuze and sort
|
55
|
+
# # results by relevance.
|
56
|
+
# request.search :books, power: 'author:lacan and not fiction',
|
57
|
+
# sort: 'relevancerank'
|
58
|
+
#
|
59
|
+
def search(index, query_or_params = nil)
|
60
|
+
params = case query_or_params
|
61
|
+
when String
|
62
|
+
{ 'Keywords' => query_or_params }
|
63
|
+
else
|
64
|
+
query_or_params
|
65
|
+
end
|
66
|
+
build! params.merge! 'Operation' => 'ItemSearch',
|
67
|
+
'SearchIndex' => Utils.camelize(index.to_s)
|
68
|
+
|
69
|
+
get
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the Addressable::URI URL of the Product Advertising API
|
73
|
+
# request.
|
74
|
+
def url
|
75
|
+
Addressable::URI.new :scheme => 'http',
|
76
|
+
:host => endpoint.host,
|
77
|
+
:path => '/onca/xml',
|
78
|
+
:query_values => parameters
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def default_parameters
|
84
|
+
super.merge 'AssociateTag' => endpoint.tag,
|
85
|
+
'Service' => 'AWSECommerceService',
|
86
|
+
'Version' => '2011-08-01'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
module Signature
|
4
|
+
# Internal: Middleware that signs REST requests to various Amazon API
|
5
|
+
# endpoints.
|
6
|
+
class Authentication < Faraday::Middleware
|
7
|
+
# Initializes the middleware.
|
8
|
+
#
|
9
|
+
# app - An Object that responds to `call` and returns a
|
10
|
+
# Faraday::Response.
|
11
|
+
# secret - The String Amazon AWS access secret key.
|
12
|
+
def initialize(app, secret)
|
13
|
+
@secret = secret
|
14
|
+
super app
|
15
|
+
end
|
16
|
+
|
17
|
+
# Signs the request.
|
18
|
+
#
|
19
|
+
# env - A Hash that contains info about the request.
|
20
|
+
#
|
21
|
+
# Returns an Object that responds to `call` and returns a
|
22
|
+
# Faraday::Response.
|
23
|
+
def call(env)
|
24
|
+
builder = Builder.new env, @secret
|
25
|
+
builder.timestamp.sort_query.sign
|
26
|
+
|
27
|
+
@app.call builder.env
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
module Signature
|
4
|
+
# Internal: Signs a request to an Amazon API with an HMAC-SHA256
|
5
|
+
# signature.
|
6
|
+
class Builder
|
7
|
+
# Returns a Hash that contains info about the request.
|
8
|
+
attr :env
|
9
|
+
|
10
|
+
# Returns the String Amazon AWS access secret key.
|
11
|
+
attr :secret
|
12
|
+
|
13
|
+
# Initializes a new Builder.
|
14
|
+
#
|
15
|
+
# env - A Hash that contains info about the request.
|
16
|
+
def initialize(env, secret)
|
17
|
+
env[:url] = Addressable::URI.parse env[:url]
|
18
|
+
@env, @secret = env, secret
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the String name of the HTTP method used by the request.
|
22
|
+
def method
|
23
|
+
env[:method].to_s.upcase
|
24
|
+
end
|
25
|
+
|
26
|
+
# Signs the request.
|
27
|
+
#
|
28
|
+
# Returns self.
|
29
|
+
def sign
|
30
|
+
url.query = url.query.to_s + "&Signature=#{Utils.encode signature}"
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a String signature.
|
35
|
+
def signature
|
36
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
37
|
+
hash = OpenSSL::HMAC.digest sha256, secret, string_to_sign
|
38
|
+
|
39
|
+
Base64.encode64(hash).chomp
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sorts the URL query values of the request.
|
43
|
+
#
|
44
|
+
# Returns self.
|
45
|
+
def sort_query
|
46
|
+
url.query_values = url.query_values
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns a String to sign based on pseudo-grammar specified by Amazon.
|
51
|
+
def string_to_sign
|
52
|
+
[method, url.host, url.path, url.query].join "\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Timestamps the request.
|
56
|
+
#
|
57
|
+
# Returns self.
|
58
|
+
def timestamp
|
59
|
+
url.query = url.query.to_s + "&Timestamp=#{Utils.encode Time.now.utc.iso8601}"
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the Addressable::URI URL of the request.
|
64
|
+
def url
|
65
|
+
env[:url]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Request
|
3
|
+
module Utils
|
4
|
+
# Camelizes a value.
|
5
|
+
#
|
6
|
+
# val - A String value.
|
7
|
+
#
|
8
|
+
# Returns an upper-camelcased String.
|
9
|
+
def self.camelize(val)
|
10
|
+
val.split('_').map(&:capitalize).join
|
11
|
+
end
|
12
|
+
|
13
|
+
# Percent encodes a URI component.
|
14
|
+
#
|
15
|
+
# component - The String URI component to encode.
|
16
|
+
#
|
17
|
+
# Returns the String encoded component.
|
18
|
+
def self.encode(component)
|
19
|
+
Addressable::URI.encode_component \
|
20
|
+
component, Addressable::URI::CharacterClasses::UNRESERVED
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Vacuum
|
2
|
+
module Response
|
3
|
+
# An Amazon Web Services (AWS) API response.
|
4
|
+
class Base
|
5
|
+
# Gets/Sets the String response body.
|
6
|
+
attr_accessor :body
|
7
|
+
|
8
|
+
# Gets/Sets the Integer HTTP response code.
|
9
|
+
attr_accessor :code
|
10
|
+
|
11
|
+
# Initializes a new Response.
|
12
|
+
#
|
13
|
+
# body - The String response body.
|
14
|
+
# code - An HTTP response code that responds to to_i.
|
15
|
+
def initialize(body, code)
|
16
|
+
@body, @code = body, code.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
# Queries the response.
|
20
|
+
#
|
21
|
+
# query - String attribute to be queried.
|
22
|
+
#
|
23
|
+
# Yields matching nodes to a given block if one is given.
|
24
|
+
#
|
25
|
+
# Returns an Array of matching nodes or the return values of the yielded
|
26
|
+
# block if latter was given.
|
27
|
+
def find(query)
|
28
|
+
path = if xml.namespaces.empty?
|
29
|
+
"//#{query}"
|
30
|
+
else
|
31
|
+
"//xmlns:#{query}"
|
32
|
+
end
|
33
|
+
|
34
|
+
xml.xpath(path).map do |node|
|
35
|
+
hsh = Utils.xml_to_hash node
|
36
|
+
block_given? ? yield(hsh) : hsh
|
37
|
+
end
|
38
|
+
end
|
39
|
+
alias [] find
|
40
|
+
|
41
|
+
# Returns a Hash representation of the response.
|
42
|
+
def to_hash
|
43
|
+
Utils.xml_to_hash xml
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns whether the HTTP response is OK.
|
47
|
+
def valid?
|
48
|
+
code == 200
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an XML document.
|
52
|
+
def xml
|
53
|
+
@xml ||= Nokogiri::XML.parse @body
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|