dpickett-amazon_associate 0.6.5 → 0.7.0

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/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  *.swp
2
+ tags
data/README.rdoc CHANGED
@@ -12,6 +12,9 @@ If in the future, there is a change in REST XML output structure,
12
12
  no changes will be required on <tt>amazon-ecs</tt> library,
13
13
  instead you just need to change the element path.
14
14
 
15
+ NOTE: You must now specify a secret key to support request signing as
16
+ required by Amazon.
17
+
15
18
  Version: 0.6.1
16
19
 
17
20
  == WANTS
@@ -28,6 +31,7 @@ Version: 0.6.1
28
31
  # set the default options; options will be camelized and converted to REST request parameters.
29
32
  AmazonAssociate::Request.configure do |options|
30
33
  options[:aWS_access_key_id] = [your developer token]
34
+ options[:secrety_key] = [your secret key]
31
35
  end
32
36
 
33
37
  # options provided on method call will merge with the default options
@@ -92,6 +96,7 @@ Filesystem caching is now available.
92
96
 
93
97
  AmazonAssociate::Request.configure do |options|
94
98
  options[:aWS_access_key_id] = [your developer token]
99
+ options[:scret_key] = [your secret key]
95
100
  options[:caching_strategy] = :filesystem
96
101
  options[:caching_options] = {
97
102
  :disk_quota => 200,
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 5
2
+ :patch: 0
3
3
  :major: 0
4
- :minor: 6
4
+ :minor: 7
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{amazon_associate}
5
- s.version = "0.6.5"
5
+ s.version = "0.7.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Dan Pickett"]
9
- s.date = %q{2009-06-30}
9
+ s.date = %q{2009-08-01}
10
10
  s.description = %q{interfaces with Amazon Associate's API using Hpricot}
11
11
  s.email = %q{dpickett@enlightsolutions.com}
12
12
  s.extra_rdoc_files = [
@@ -40,11 +40,10 @@ Gem::Specification.new do |s|
40
40
  "test/test_helper.rb",
41
41
  "test/utilities/filesystem_test_helper.rb"
42
42
  ]
43
- s.has_rdoc = true
44
43
  s.homepage = %q{http://github.com/dpickett/amazon_associate}
45
44
  s.rdoc_options = ["--charset=UTF-8"]
46
45
  s.require_paths = ["lib"]
47
- s.rubygems_version = %q{1.3.2}
46
+ s.rubygems_version = %q{1.3.5}
48
47
  s.summary = %q{Amazon Associates API Interface using Hpricot}
49
48
  s.test_files = [
50
49
  "test/amazon_associate/browse_node_lookup_test.rb",
@@ -1,5 +1,10 @@
1
1
  $:.unshift(File.dirname(__FILE__))
2
2
 
3
+ require "base64"
4
+ require "hmac-sha2"
5
+ require "digest/sha1"
6
+ require "digest/sha2"
7
+
3
8
  require "amazon_associate/request"
4
9
  require "amazon_associate/element"
5
10
  require "amazon_associate/response"
@@ -17,7 +17,7 @@ module AmazonAssociate
17
17
 
18
18
  def cache(request, response)
19
19
  path = self.cache_path
20
- cached_filename = Digest::SHA1.hexdigest(response.request_url)
20
+ cached_filename = Digest::SHA1.hexdigest(request)
21
21
  cached_folder = cached_filename[0..2]
22
22
 
23
23
  FileUtils.mkdir_p(File.join(path, cached_folder, cached_folder))
@@ -9,7 +9,7 @@ rescue LoadError
9
9
  end
10
10
 
11
11
  #--
12
- # Copyright (c) 2006 Herryanto Siatono, Pluit Solutions
12
+ # Copyright (c) 2009 Dan Pickett, Enlight Solutions
13
13
  #
14
14
  # Permission is hereby granted, free of charge, to any person obtaining
15
15
  # a copy of this software and associated documentation files (the
@@ -33,12 +33,12 @@ end
33
33
  module AmazonAssociate
34
34
  class Request
35
35
 
36
- SERVICE_URLS = {:us => "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService",
37
- :uk => "http://webservices.amazon.co.uk/onca/xml?Service=AWSECommerceService",
38
- :ca => "http://webservices.amazon.ca/onca/xml?Service=AWSECommerceService",
39
- :de => "http://webservices.amazon.de/onca/xml?Service=AWSECommerceService",
40
- :jp => "http://webservices.amazon.co.jp/onca/xml?Service=AWSECommerceService",
41
- :fr => "http://webservices.amazon.fr/onca/xml?Service=AWSECommerceService"
36
+ SERVICE_URLS = {:us => "http://webservices.amazon.com",
37
+ :uk => "http://webservices.amazon.co.uk",
38
+ :ca => "http://webservices.amazon.ca",
39
+ :de => "http://webservices.amazon.de",
40
+ :jp => "http://webservices.amazon.co.jp",
41
+ :fr => "http://webservices.amazon.fr"
42
42
  }
43
43
 
44
44
  # The sort types available to each product search index.
@@ -229,29 +229,38 @@ module AmazonAssociate
229
229
  # Generic send request to ECS REST service. You have to specify the :operation parameter.
230
230
  def self.send_request(opts)
231
231
  opts = self.options.merge(opts) if self.options
232
- request_url = prepare_url(opts)
232
+ unsigned_url = prepare_unsigned_url(opts)
233
233
  response = nil
234
234
 
235
235
  if caching_enabled?
236
236
  AmazonAssociate::CacheFactory.sweep(self.options[:caching_strategy])
237
237
 
238
- res = AmazonAssociate::CacheFactory.get(request_url, self.options[:caching_strategy])
239
- response = Response.new(res, request_url) unless res.nil?
238
+ res = AmazonAssociate::CacheFactory.get(unsigned_url, self.options[:caching_strategy])
239
+ response = Response.new(res, unsigned_url) unless res.nil?
240
240
  end
241
241
 
242
242
  if !caching_enabled? || response.nil?
243
+ request_url = prepare_signed_url(opts)
243
244
  log "Request URL: #{request_url}"
244
245
  res = Net::HTTP.get_response(URI::parse(request_url))
246
+
245
247
  unless res.kind_of? Net::HTTPSuccess
246
248
  raise AmazonAssociate::RequestError, "HTTP Response: #{res.code} #{res.message}"
247
249
  end
250
+
248
251
  response = Response.new(res.body, request_url)
249
- cache_response(request_url, response, self.options[:caching_strategy]) if caching_enabled?
252
+ response.unsigned_url = unsigned_url
253
+
254
+ if caching_enabled?
255
+ cache_response(unsigned_url, response, self.options[:caching_strategy])
256
+ end
250
257
  end
251
258
 
252
259
  response
253
260
  end
254
261
 
262
+ attr_accessor :request_url, :unsigned_url
263
+
255
264
  protected
256
265
  def self.log(s)
257
266
  return unless self.debug
@@ -264,21 +273,70 @@ module AmazonAssociate
264
273
  end
265
274
  end
266
275
 
267
- private
268
- def self.prepare_url(opts)
276
+ private
277
+ def self.get_service_url(opts)
269
278
  country = opts.delete(:country)
270
279
  country = (country.nil?) ? "us" : country
271
- request_url = SERVICE_URLS[country.to_sym]
272
- raise AmazonAssociate::RequestError, "Invalid country \"#{country}\"" unless request_url
280
+ url = SERVICE_URLS[country.to_sym]
281
+
282
+ raise AmazonAssociate::RequestError, "Invalid country \"#{country}\"" unless url
283
+ url
284
+ end
285
+
286
+ def self.prepare_unsigned_url(opts)
287
+ url = get_service_url(opts) + "/onca/xml"
273
288
 
274
289
  qs = ""
275
290
  opts.each {|k,v|
276
291
  next unless v
277
- next if [:caching_options, :caching_strategy].include?(k)
292
+ next if [:caching_options, :caching_strategy, :secret_key].include?(k)
278
293
  v = v.join(",") if v.is_a? Array
279
294
  qs << "&#{camelize(k.to_s)}=#{URI.encode(v.to_s)}"
280
295
  }
281
- "#{request_url}#{qs}"
296
+
297
+ @unsigned_url = "#{url}#{qs}"
298
+ end
299
+
300
+ def self.prepare_signed_url(opts)
301
+ url = get_service_url(opts) + "/onca/xml"
302
+
303
+ unencoded_key_value_strings = []
304
+ encoded_key_value_strings = []
305
+ opts[:timestamp] = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S") + ".000Z"
306
+ opts[:service] = "AWSECommerceService"
307
+ opts[:version] = "2009-01-01"
308
+ sort_parameters(opts).each do |p|
309
+ next if p[1].nil?
310
+ next if [:caching_options, :caching_strategy, :secret_key].include?(p[0])
311
+
312
+
313
+ encoded_value = CGI.escape(p[1].to_s)
314
+
315
+ encoded_key_value_strings << camelize(p[0].to_s ) + "=" + encoded_value
316
+ end
317
+
318
+ string_to_sign =
319
+ "GET
320
+ #{get_service_url(opts).gsub("http://", "")}
321
+ /onca/xml
322
+ #{encoded_key_value_strings.join("&")}"
323
+
324
+ signature = sign_string(string_to_sign)
325
+ encoded_key_value_strings << "Signature=" + signature
326
+
327
+ "#{url}?#{encoded_key_value_strings.join("&")}"
328
+ end
329
+
330
+ def self.sort_parameters(opts)
331
+ key_value_strings = []
332
+ opts.sort {|a, b| camelize(a) <=> camelize(b) }
333
+ end
334
+
335
+ def self.sign_string(string_to_sign)
336
+ sha1 = HMAC::SHA256.digest(self.options[:secret_key], string_to_sign)
337
+
338
+ #Base64 encoding adds a linefeed to the end of the string so chop the last character!
339
+ CGI.escape(Base64.encode64(sha1).chomp)
282
340
  end
283
341
 
284
342
  def self.camelize(s)
@@ -2,7 +2,7 @@ module AmazonAssociate
2
2
  # Response object returned after a REST call to Amazon service.
3
3
  class Response
4
4
 
5
- attr_accessor :request_url
5
+ attr_accessor :request_url, :unsigned_url
6
6
  # XML input is in string format
7
7
  def initialize(xml, request_url)
8
8
  @doc = Hpricot(xml)
@@ -71,4 +71,4 @@ module AmazonAssociate
71
71
  @total_pages
72
72
  end
73
73
  end
74
- end
74
+ end
@@ -74,7 +74,7 @@ class AmazonAssociate::CachingStrategy::FilesystemTest < Test::Unit::TestCase
74
74
  get_cache_directory
75
75
  get_valid_caching_options
76
76
  @resp = AmazonAssociate::Request.item_lookup("0974514055")
77
- @filename = Digest::SHA1.hexdigest(@resp.request_url)
77
+ @filename = Digest::SHA1.hexdigest(@resp.unsigned_url)
78
78
  end
79
79
 
80
80
  teardown do
@@ -122,11 +122,6 @@ class AmazonAssociate::CachingStrategy::FilesystemTest < Test::Unit::TestCase
122
122
  do_request
123
123
  end
124
124
 
125
- should "return the same response as the original request" do
126
- original = @resp.doc.to_s
127
- do_request
128
- assert_equal(original, @resp.doc.to_s)
129
- end
130
125
  end
131
126
 
132
127
  context "sweeping cached requests" do
data/test/test_helper.rb CHANGED
@@ -11,6 +11,7 @@ require 'amazon_associate'
11
11
 
12
12
  AmazonAssociate::Request.configure do |options|
13
13
  options[:aWS_access_key_id] = ENV["AWS_ACCESS_KEY"] || ""
14
+ options[:secret_key] = ENV["AWS_SECRET_KEY"] || ""
14
15
 
15
16
  #raise exception if user has not entered their access key
16
17
  if options[:aWS_access_key_id] == ""
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dpickett-amazon_associate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Pickett
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-30 00:00:00 -07:00
12
+ date: 2009-08-01 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -48,8 +48,9 @@ files:
48
48
  - test/amazon_associate/request_test.rb
49
49
  - test/test_helper.rb
50
50
  - test/utilities/filesystem_test_helper.rb
51
- has_rdoc: true
51
+ has_rdoc: false
52
52
  homepage: http://github.com/dpickett/amazon_associate
53
+ licenses:
53
54
  post_install_message:
54
55
  rdoc_options:
55
56
  - --charset=UTF-8
@@ -70,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
71
  requirements: []
71
72
 
72
73
  rubyforge_project:
73
- rubygems_version: 1.2.0
74
+ rubygems_version: 1.3.5
74
75
  signing_key:
75
76
  specification_version: 3
76
77
  summary: Amazon Associates API Interface using Hpricot