Skip to content

Instantly share code, notes, and snippets.

@jonahb
Created February 18, 2015 21:08
Show Gist options
  • Save jonahb/09c573c37ddcfcb8406d to your computer and use it in GitHub Desktop.
Save jonahb/09c573c37ddcfcb8406d to your computer and use it in GitHub Desktop.
require 'uri'
require 'base64'
require 'openssl'
require 'time'
require 'net/http'
require 'base64'
require 'nokogiri'
module AmazonProductAdvertising
class Error < StandardError
attr_reader :code
def initialize(message, code)
super(message)
@code = code
end
end
class Client
APIVersion = '2011-08-01'
EndpointPath = '/onca/xml'
LocaleToHost = {
'US' => 'webservices.amazon.com'
}
PrefixToNamespace = {
'a' => 'http://webservices.amazon.com/AWSECommerceService/2011-08-01',
'e' => 'http://ecs.amazonaws.com/doc/2011-08-01/'
}
attr_reader :aws_access_key_id
attr_reader :aws_secret_access_key
attr_reader :associate_tag
attr_reader :locale
attr_reader :secure
def initialize(aws_access_key_id, aws_secret_access_key, associate_tag, locale: 'US', secure: false)
@aws_access_key_id = aws_access_key_id
@aws_secret_access_key = aws_secret_access_key
@associate_tag = associate_tag
@secure = secure
unless LocaleToHost.key?(locale)
raise ArgumentError, "Invalid locale: '#{locale}'"
end
@locale = locale
end
def item_search(search_index, params = {})
invoke 'ItemSearch', params.merge('SearchIndex' => search_index)
end
def item_lookup(item_id, params = {})
invoke 'ItemLookup', params.merge('ItemId' => item_id)
end
private
def invoke(operation, params = {})
params = params.merge(
'AssociateTag' => associate_tag,
'AWSAccessKeyId' => aws_access_key_id,
'Operation' => operation,
'Service' => 'AWSECommerceService',
'Timestamp' => Time.now.utc.iso8601,
'Version' => APIVersion)
scheme = secure ? 'https' : 'http'
query = sign(params)
uri = URI("#{scheme}://#{host}#{EndpointPath}?#{query}")
handle_response Net::HTTP.get_response(uri)
end
def handle_response(response)
doc = Nokogiri::XML(response.body)
error = doc.at_xpath('//a:Error | //e:Error', PrefixToNamespace)
if error
message = error % 'Message'
code = error % 'Code'
raise Error.new(message && message.text, code && code.text)
end
doc
end
def sign(params)
sorted_params = params.sort { |(k1, _), (k2, _)| k1.bytes <=> k2.bytes }
query = uri_encode_params(sorted_params)
message = ['GET', host, EndpointPath, query].join("\n")
digest = OpenSSL::Digest::SHA256.new
signature = OpenSSL::HMAC.digest(digest, aws_secret_access_key, message)
param = uri_encode(Base64.encode64(signature).chomp)
query + "&Signature=#{param}"
end
def host
LocaleToHost[locale]
end
def uri_encode_params(params)
params.collect do |k, v|
"#{uri_encode(k)}=#{uri_encode(v)}"
end.join('&')
end
def uri_encode(string)
URI.encode_www_form_component(string).gsub('+', '%20')
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment