completelynovel-amazon-product-advertising-api 0.1.0 → 0.2.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/EXAMPLE.txt CHANGED
@@ -3,8 +3,10 @@ Basic usage
3
3
 
4
4
  require 'rubygems'
5
5
  require 'amazon_product_advertising_api'
6
- # Setup your API key - in an initializer or something like that
6
+
7
+ # Setup your API keys - in an initializer or something like that
7
8
  AmazonProductAdvertisingApi::Base.access_key_id = "<insert api key here>"
9
+ AmazonProductAdvertisingApi::Base.secret_access_key = "<insert secret access key here>"
8
10
 
9
11
  # Setup Associates IDs for whichever regions you're selling to
10
12
  AmazonProductAdvertisingApi::Base.associate_ids.uk = "<insert UK Associate ID here>"
data/README.rdoc CHANGED
@@ -77,7 +77,6 @@ intended rules.
77
77
  = TODO
78
78
  - Implement the rest of the Operations (Cart*, Customer*, Help, List*, Seller*,
79
79
  Tag*, Transaction* and Vehicle*).
80
- - Implement HMAC before the authentication requirement kicks in in August 2009.
81
80
  - Implement batch and multiple operation requests, abstracted away from the
82
81
  user within the dsl.
83
82
  - Some sort of internal caching mechanism.
@@ -88,8 +87,8 @@ intended rules.
88
87
  = Obtaining
89
88
  The main repository is at github but there is also a rubyforge project.
90
89
 
91
- https://github.com/completelynovel/amazon-product-advertising-api/tree
92
- http://amazon-pa-api.rubyforge.org/
90
+ - https://github.com/completelynovel/amazon-product-advertising-api/tree
91
+ - http://amazon-pa-api.rubyforge.org/
93
92
 
94
93
 
95
94
  = Credits
data/Rakefile CHANGED
@@ -14,12 +14,14 @@ end
14
14
  begin
15
15
  require 'jeweler'
16
16
  Jeweler::Tasks.new do |gemspec|
17
- gemspec.name = "amazon-product-advertising-api"
18
- gemspec.summary = "A nice rubyish interface to the Amazon Product Advertising API."
19
- gemspec.email = "jon@completelynovel.com"
20
- gemspec.homepage = "http://github.com/completelynovel/amazon-product-advertising-api"
17
+ gemspec.name = "amazon-product-advertising-api"
18
+ gemspec.summary = "A nice rubyish interface to the Amazon Product Advertising API."
19
+ gemspec.email = "jon@completelynovel.com"
20
+ gemspec.homepage = "http://github.com/completelynovel/amazon-product-advertising-api"
21
21
  gemspec.description = "A nice rubyish interface to the Amazon Product Advertising API, formerly known as the Associates Web Service and before that the Amazon E-Commerce Service."
22
- gemspec.authors = ["Jon Gilbraith"]
22
+ gemspec.authors = ["Jon Gilbraith"]
23
+ gemspec.add_dependency("hpricot")
24
+ gemspec.add_dependency("ruby-hmac")
23
25
  end
24
26
  rescue LoadError
25
27
  puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{amazon-product-advertising-api}
5
- s.version = "0.1.0"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Jon Gilbraith"]
9
- s.date = %q{2009-07-06}
9
+ s.date = %q{2009-07-30}
10
10
  s.description = %q{A nice rubyish interface to the Amazon Product Advertising API, formerly known as the Associates Web Service and before that the Amazon E-Commerce Service.}
11
11
  s.email = %q{jon@completelynovel.com}
12
12
  s.extra_rdoc_files = [
@@ -40,8 +40,14 @@ Gem::Specification.new do |s|
40
40
  s.specification_version = 2
41
41
 
42
42
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
43
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
44
+ s.add_runtime_dependency(%q<ruby-hmac>, [">= 0"])
43
45
  else
46
+ s.add_dependency(%q<hpricot>, [">= 0"])
47
+ s.add_dependency(%q<ruby-hmac>, [">= 0"])
44
48
  end
45
49
  else
50
+ s.add_dependency(%q<hpricot>, [">= 0"])
51
+ s.add_dependency(%q<ruby-hmac>, [">= 0"])
46
52
  end
47
53
  end
@@ -1,8 +1,8 @@
1
1
  module AmazonProductAdvertisingApi #:nodoc:
2
2
 
3
3
  # This is the main base class where you define your config data. Setup like so:
4
- # AmazonProductAdvertisingApi.base.access_key_id = <your Amazon AccessKeyId>
5
- # There is no support for the secret key yet, so it's just your access key for now.
4
+ # AmazonProductAdvertisingApi.base.access_key_id = <your Amazon AccessKeyId>
5
+ # AmazonProductAdvertisingApi.base.secret_access_key = <your Amazon SecretAccessKey>
6
6
  #
7
7
  # You also setup your Associates codes for different regions here (the right one is supplied)
8
8
  # based on what region you are requesting for.
@@ -11,6 +11,8 @@ module AmazonProductAdvertisingApi #:nodoc:
11
11
  class Base
12
12
 
13
13
  cattr_accessor :access_key_id
14
+
15
+ cattr_accessor :secret_access_key
14
16
 
15
17
  cattr_accessor :associate_ids
16
18
  @@associate_ids = Struct.new(:ca, :de, :fr, :jp, :uk, :us).new
@@ -41,13 +41,15 @@ module AmazonProductAdvertisingApi #:nodoc:
41
41
  attr_accessor :errors
42
42
 
43
43
  SERVICE_URLS = {
44
- :us => 'http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService',
45
- :uk => 'http://ecs.amazonaws.co.uk/onca/xml?Service=AWSECommerceService',
46
- :ca => 'http://ecs.amazonaws.ca/onca/xml?Service=AWSECommerceService',
47
- :de => 'http://ecs.amazonaws.de/onca/xml?Service=AWSECommerceService',
48
- :jp => 'http://ecs.amazonaws.jp/onca/xml?Service=AWSECommerceService',
49
- :fr => 'http://ecs.amazonaws.fr/onca/xml?Service=AWSECommerceService'
44
+ :us => 'http://ecs.amazonaws.com/onca/xml',
45
+ :uk => 'http://ecs.amazonaws.co.uk/onca/xml',
46
+ :ca => 'http://ecs.amazonaws.ca/onca/xml',
47
+ :de => 'http://ecs.amazonaws.de/onca/xml',
48
+ :jp => 'http://ecs.amazonaws.jp/onca/xml',
49
+ :fr => 'http://ecs.amazonaws.fr/onca/xml'
50
50
  }
51
+
52
+ API_VERSION = "2009-01-06"
51
53
 
52
54
  def initialize
53
55
  self.response = AmazonProductAdvertisingApi::Operations::Base::Element.new
@@ -57,16 +59,31 @@ module AmazonProductAdvertisingApi #:nodoc:
57
59
  # This takes care of building request, performing it, storing the results, checking for errors then parsing the data (if the request was valid).
58
60
  def query_amazon(params)
59
61
  request_params = {}
60
- request_params["AWSAccessKeyId"] = AmazonProductAdvertisingApi::Base.access_key_id
61
- request_params["Operation"] = self.operation
62
- request_params["AssociateTag"] = AmazonProductAdvertisingApi::Base.associate_ids.send(self.region) unless AmazonProductAdvertisingApi::Base.associate_ids.send(self.region).nil?
62
+ request_params["Service"] = "AWSECommerceService"
63
+ request_params["SignatureVersion"] = 2
64
+ request_params["SignatureMethod"] = "HmacSHA256"
65
+ request_params["Timestamp"] = Time.now.gmtime.iso8601
66
+ request_params["AWSAccessKeyId"] = AmazonProductAdvertisingApi::Base.access_key_id
67
+ request_params["Operation"] = self.operation
68
+ request_params["AssociateTag"] = AmazonProductAdvertisingApi::Base.associate_ids.send(self.region) unless AmazonProductAdvertisingApi::Base.associate_ids.send(self.region).nil?
69
+ request_params["Version"] = API_VERSION
63
70
  request_params.merge!(params)
71
+
72
+ # Process all params - make sure they're all strings, camelize and escape (where appropriate)
73
+ request_params = request_params.collect { |var, val| [var.to_s.camelize, val.to_s] }
74
+ request_params = request_params.collect { |var, val| [var, CGI::escape(val).gsub('+', '%20')] }
64
75
 
65
- self.request_uri = "#{SERVICE_URLS[self.region]}&#{request_params.collect { |var, val| var.to_s.camelize + "=" + val.to_s }.join("&")}"
66
- self.request_uri = URI.parse(URI.escape(self.request_uri))
67
-
76
+ # Assemble into a full request string
77
+ unsigned_uri = URI.parse("#{SERVICE_URLS[self.region]}?#{request_params.sort { |a, b| a[0] <=> b[0] }.collect { |var, val| var + "=" + val }.join("&")}")
78
+
79
+ # Generate hmac
80
+ hmac = HMAC::SHA256.new(AmazonProductAdvertisingApi::Base.secret_access_key)
81
+ hmac.update("GET\n#{unsigned_uri.host}\n#{unsigned_uri.path}\n#{unsigned_uri.query}")
82
+
83
+ self.request_uri = URI.parse("#{unsigned_uri}&Signature=#{Base64.encode64(hmac.digest).chomp}")
84
+
68
85
  result = Net::HTTP::get_response(self.request_uri)
69
- raise("Error connecting to Amazon") if !result.kind_of?(Net::HTTPSuccess)
86
+ raise("Error connecting to Amazon - #{result.to_s}") if !result.kind_of?(Net::HTTPSuccess)
70
87
 
71
88
  # Store away the raw data for debugging or if more direct access is required
72
89
  self.raw_data = result.body
@@ -1,67 +1,67 @@
1
1
  module AmazonProductAdvertisingApi #:nodoc:
2
-
3
- # Some extensions to the Class class.
4
- class Class
2
+ module CoreExtensions
3
+ module Class
5
4
 
6
- # Pleasant syntax for Class attribute readers.
7
- def cattr_reader(sym)
8
- class_eval(<<-EOS, __FILE__, __LINE__)
9
- unless defined? @@#{sym} # unless defined? @@hair_colors
10
- @@#{sym} = nil # @@hair_colors = nil
11
- end # end
12
- #
13
- def self.#{sym} # def self.hair_colors
14
- @@#{sym} # @@hair_colors
15
- end # end
16
- #
17
- def #{sym} # def hair_colors
18
- @@#{sym} # @@hair_colors
19
- end # end
20
- EOS
21
- end
5
+ # Pleasant syntax for Class attribute readers.
6
+ def cattr_reader(sym)
7
+ class_eval(<<-EOS, __FILE__, __LINE__)
8
+ unless defined? @@#{sym} # unless defined? @@hair_colors
9
+ @@#{sym} = nil # @@hair_colors = nil
10
+ end # end
11
+ #
12
+ def self.#{sym} # def self.hair_colors
13
+ @@#{sym} # @@hair_colors
14
+ end # end
15
+ #
16
+ def #{sym} # def hair_colors
17
+ @@#{sym} # @@hair_colors
18
+ end # end
19
+ EOS
20
+ end
22
21
 
23
- # Pleasant syntax for Class attribute writers.
24
- def cattr_writer(sym)
25
- class_eval(<<-EOS, __FILE__, __LINE__)
26
- unless defined? @@#{sym} # unless defined? @@hair_colors
27
- @@#{sym} = nil # @@hair_colors = nil
28
- end # end
29
- #
30
- def self.#{sym}=(obj) # def self.hair_colors=(obj)
31
- @@#{sym} = obj # @@hair_colors = obj
32
- end # end
33
- EOS
34
- end
22
+ # Pleasant syntax for Class attribute writers.
23
+ def cattr_writer(sym)
24
+ class_eval(<<-EOS, __FILE__, __LINE__)
25
+ unless defined? @@#{sym} # unless defined? @@hair_colors
26
+ @@#{sym} = nil # @@hair_colors = nil
27
+ end # end
28
+ #
29
+ def self.#{sym}=(obj) # def self.hair_colors=(obj)
30
+ @@#{sym} = obj # @@hair_colors = obj
31
+ end # end
32
+ EOS
33
+ end
34
+
35
+ # Pleasant syntax for Class attribute accessors.
36
+ def cattr_accessor(sym)
37
+ cattr_reader(sym)
38
+ cattr_writer(sym)
39
+ end
35
40
 
36
- # Pleasant syntax for Class attribute accessors.
37
- def cattr_accessor(sym)
38
- cattr_reader(sym)
39
- cattr_writer(sym)
40
41
  end
41
42
 
42
- end
43
+ # Some extensions to the String class.
44
+ module String
43
45
 
44
- # Some extensions to the String class.
45
- class String
46
+ # Converts strings from under_score format to CamelCase
47
+ def camelize(first_letter_in_uppercase = true)
48
+ if first_letter_in_uppercase
49
+ self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
50
+ else
51
+ self.first + camelize(self)[1..-1]
52
+ end
53
+ end
46
54
 
47
- # Converts strings from under_score format to CamelCase
48
- def camelize(first_letter_in_uppercase = true)
49
- if first_letter_in_uppercase
50
- self.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
51
- else
52
- self.first + camelize(self)[1..-1]
55
+ # Converts strings from CamelCase format to under_score.
56
+ def underscore
57
+ self.to_s.gsub(/::/, '/').
58
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
59
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
60
+ tr("-", "_").
61
+ downcase
53
62
  end
54
- end
55
63
 
56
- # Converts strings from CamelCase format to under_score.
57
- def underscore
58
- self.to_s.gsub(/::/, '/').
59
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
60
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
61
- tr("-", "_").
62
- downcase
63
64
  end
64
-
65
- end
66
65
 
66
+ end
67
67
  end
@@ -1,7 +1,16 @@
1
1
  require 'net/http'
2
+ require 'cgi'
2
3
  require 'hpricot'
4
+ require 'time'
5
+ require 'hmac'
6
+ require 'hmac-sha2'
7
+ require 'base64'
3
8
 
4
9
  require 'amazon_product_advertising_api/support'
10
+
11
+ Class.send(:include, AmazonProductAdvertisingApi::CoreExtensions::Class)
12
+ String.send(:include, AmazonProductAdvertisingApi::CoreExtensions::String)
13
+
5
14
  require 'amazon_product_advertising_api/base'
6
15
  require 'amazon_product_advertising_api/response_elements'
7
16
  require 'amazon_product_advertising_api/operations/base'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: completelynovel-amazon-product-advertising-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Gilbraith
@@ -9,10 +9,29 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-06 00:00:00 -07:00
12
+ date: 2009-07-30 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies: []
15
-
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-hmac
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
16
35
  description: A nice rubyish interface to the Amazon Product Advertising API, formerly known as the Associates Web Service and before that the Amazon E-Commerce Service.
17
36
  email: jon@completelynovel.com
18
37
  executables: []
@@ -38,6 +57,7 @@ files:
38
57
  - lib/amazon_product_advertising_api/support.rb
39
58
  has_rdoc: true
40
59
  homepage: http://github.com/completelynovel/amazon-product-advertising-api
60
+ licenses:
41
61
  post_install_message:
42
62
  rdoc_options:
43
63
  - --charset=UTF-8
@@ -58,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
78
  requirements: []
59
79
 
60
80
  rubyforge_project:
61
- rubygems_version: 1.2.0
81
+ rubygems_version: 1.3.5
62
82
  signing_key:
63
83
  specification_version: 2
64
84
  summary: A nice rubyish interface to the Amazon Product Advertising API.