dpickett-amazon_associate 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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