vacuum 3.0.0 → 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93f97f47ef585594e52efc75ac3824e47992ad0ac6cbd688f05363579c9feff3
4
- data.tar.gz: 4d0399b9b897cd955cb3c2739a3b2fcb4e393c6ee092270d21d28dab8ee503fc
3
+ metadata.gz: 61ab29cce33477553968b592986abbc5de22f4885945032313a08dc89174c5c4
4
+ data.tar.gz: 01b3fddbc4f06899df06c595ff28dc0f812cca8d1b8604771614a077129d4a99
5
5
  SHA512:
6
- metadata.gz: eac6cca761cd2ccc75e7eb9d626398505c06400116efe5bb765e5112debb0f3b22da3dab840aa1bbde19de752d841bd836029c6c1bb7f3b12738d74bfecd0e3b
7
- data.tar.gz: 8571a8e9c4b64b71038bf7713364c8b56bca14bb08abae6743aaf2cea0a976fe1b0025432ad659b300d8d5763deb05bd9ff4200bdfcb80e52de9a2f3d3e8952c
6
+ metadata.gz: b0d2aeb0b372920cca1f68268fe91f80630ca7272e1a6dadb25677aacad9a6ae86fa3ec2056ec5419f3167c2871ed9ffaf4a37dc248b8d0bc50ce51599a08e9a
7
+ data.tar.gz: 423a1e7ccd04499c3df8a05dde1da3a25f691ef4d846f64bd59ac55af55f9783239efdd537881a0c460900cad326fdd4c50041e0ce7bb9233cd3c934861227ba
data/README.md CHANGED
@@ -1,20 +1,22 @@
1
1
  # Vacuum
2
2
 
3
- [![CircleCI](https://circleci.com/gh/hakanensari/vacuum/tree/master.svg?style=svg)](https://circleci.com/gh/hakanensari/vacuum/tree/master)
3
+ [![Build](https://github.com/hakanensari/vacuum/workflows/build/badge.svg)](https://github.com/hakanensari/vacuum/actions)
4
+ [![Maintainability](https://api.codeclimate.com/v1/badges/ffbab4aa5fedf5780332/maintainability)](https://codeclimate.com/github/hakanensari/vacuum/maintainability)
5
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/ffbab4aa5fedf5780332/test_coverage)](https://codeclimate.com/github/hakanensari/vacuum/test_coverage)
4
6
 
5
- Vacuum is a light-weight Ruby wrapper to [Amazon Product Advertising API 5.0](https://webservices.amazon.com/paapi5/documentation/). The API provides programmatic access to search and get detailed product information on the Amazon marketplaces.
7
+ Vacuum is a Ruby wrapper to [Amazon Product Advertising API 5.0](https://webservices.amazon.com/paapi5/documentation/). The API provides programmatic access to query product information on the Amazon marketplaces.
8
+
9
+ Cart Form functionality is not covered by this gem but is a primary focus on [carriage gem](https://github.com/skatkov/carriage)
10
+
11
+ You need to [register first](https://webservices.amazon.com/paapi5/documentation/register-for-pa-api.html) to use the API.
6
12
 
7
13
  ![vacuum](http://f.cl.ly/items/2k2X0e2u0G3k1c260D2u/vacuum.png)
8
14
 
9
15
  ## Usage
10
16
 
11
- Vacuum follows the nomenclature of the Product Advertising API. The examples below are based on examples in the [Amazon docs](https://webservices.amazon.com/paapi5/documentation/).
12
-
13
17
  ### Getting Started
14
18
 
15
- You need to [register as an affiliate](https://affiliate-program.amazon.com) and [apply for API access](https://affiliate-program.amazon.com/assoc_credentials/home) on each marketplace you want to query product information.
16
-
17
- Create a request with your marketplace credentials, passing the two-letter country code of the marketplace.
19
+ Create a request with your marketplace credentials. Set the marketplace by passing its two-letter country code.
18
20
 
19
21
  ```ruby
20
22
  request = Vacuum.new(marketplace: 'US',
@@ -23,17 +25,26 @@ request = Vacuum.new(marketplace: 'US',
23
25
  partner_tag: '<PARTNER_TAG>')
24
26
  ```
25
27
 
26
- Vacuum uses [HTTPI](https://github.com/savonrb/httpi) under the hood. You can swap the HTTP library it uses if you prefer an alternative one for speed or introspection.
28
+ You can now access the API using the available operations.
27
29
 
28
30
  ```ruby
29
- HTTPI.adapter = :http
31
+ response = request.search_items(title: 'lean startup')
32
+ puts response.to_h
33
+ ```
34
+
35
+ Create a persistent connection to make multiple requests.
36
+
37
+ ```ruby
38
+ request.persistent
30
39
  ```
31
40
 
32
41
  ### Operations
33
42
 
43
+ Refer to the [API docs](https://webservices.amazon.com/paapi5/documentation/) for more detailed information.
44
+
34
45
  #### GetBrowseNodes
35
46
 
36
- Given a BrowseNodeId, the `GetBrowseNodes` operation returns details about the specified browse node like name, children and ancestors depending on the resources specified in the request. The names and browse node IDs of the children and ancestor browse nodes are also returned. `GetBrowseNodes` enables you to traverse the browse node hierarchy to find a browse node.
47
+ Given a BrowseNodeId, the `GetBrowseNodes` operation returns details about the specified browse node, like name, children and ancestors, depending on the resources specified in the request. The names and browse node IDs of the children and ancestor browse nodes are also returned. `GetBrowseNodes` enables you to traverse the browse node hierarchy to find a browse node.
37
48
 
38
49
  ```ruby
39
50
  request.get_browse_nodes(
@@ -69,7 +80,7 @@ request.get_variations(
69
80
 
70
81
  #### SearchItems
71
82
 
72
- The `SearchItems` operation searches for items on Amazon based on a search query. The Amazon Product Advertising API returns up to ten items per search request.
83
+ The `SearchItems` operation searches for items on Amazon based on a search query. The API returns up to ten items per search request.
73
84
 
74
85
  ```ruby
75
86
  request.search_items(keywords: 'harry potter')
@@ -77,18 +88,30 @@ request.search_items(keywords: 'harry potter')
77
88
 
78
89
  ### Response
79
90
 
80
- The quick and dirty way to consume a response is to parse into a Ruby hash:
91
+ Consume a response by parsing it into a Ruby hash.
81
92
 
82
93
  ```ruby
83
94
  response.to_h
84
95
  ```
85
96
 
86
- You can also `#dig` into the returned Hash:
97
+ You can also `#dig` into this hash.
87
98
 
88
99
  ```ruby
89
100
  response.dig('ItemsResult', 'Items')
90
101
  ```
91
102
 
103
+ ### Logging
104
+
105
+ Write requests and reponses to a logger using the logging feature of the [HTTP gem](https://github.com/httprb/http) under the hood:
106
+
107
+ ```ruby
108
+ require 'logger'
109
+
110
+ logger = Logger.new(STDOUT)
111
+ request.use(logging: {logger: logger})
112
+ ```
113
+
114
+ ### Bring your parser
92
115
  You can extend Vacuum with a custom parser. Just swap the original with a class or module that responds to `.parse`.
93
116
 
94
117
  ```ruby
@@ -98,37 +121,55 @@ response.parse
98
121
 
99
122
  If no custom parser is set, `Vacuum::Response#parse` delegates to `#to_h`.
100
123
 
101
- ### VCR Support
124
+ ### VCR
102
125
 
103
- If you are using [VCR](https://github.com/vcr/vcr) to test an app that accesses the Product Advertising API, you can use the custom VCR matcher of Vacuum to stub requests.
126
+ If you are using [VCR](https://github.com/vcr/vcr) to test an app that accesses the API, you can use the custom VCR matcher of Vacuum to stub requests.
104
127
 
105
128
  ```ruby
106
129
  require 'vacuum/matcher'
107
130
 
108
- VCR.insert_cassette('paapi', match_requests_on: [Vacuum::Matcher])
131
+ # in your test
132
+ VCR.insert_cassette('cassette_name',
133
+ match_requests_on: [Vacuum::Matcher])
109
134
  ```
110
135
 
111
- ## Testing
136
+ In RSpec, use the `:paapi` metadata.
137
+
138
+ ```ruby
139
+ require 'vacuum/matcher'
140
+
141
+ # in your test
142
+ it 'queries Amazon', :paapi do
143
+ end
144
+ ```
112
145
 
113
- Tests should pass as-is once you install dependencies.
146
+ ## Development
147
+
148
+ Clone the repo and install dependencies.
149
+
150
+ ```sh
151
+ bundle install
152
+ ```
153
+
154
+ Tests and Rubocop should now pass as-is.
114
155
 
115
156
  ```sh
116
157
  bundle exec rake
117
158
  ```
118
159
 
119
- By default, all requests are stubbed. Use the `RECORD` env var to record new or modified interactions.
160
+ By default, the tests stub requests. Use the `RECORD` env var to record new interactions.
120
161
 
121
162
  ```sh
122
- bundle exec RECORD=true rake
163
+ RECORD=true bundle exec rake test
123
164
  ```
124
165
 
125
- You can also run tests against live data.
166
+ You can set the `LIVE` env var to run all tests against live data.
126
167
 
127
- ```shell
128
- bundle exec LIVE=true rake
168
+ ```sh
169
+ LIVE=true bundle exec rake test
129
170
  ```
130
171
 
131
- In either case, you will want to add actual API credentials to a [`locales.yml`](https://github.com/hakanensari/vacuum/blob/master/test/locales.yml.example) file in the `test` directory.
172
+ In either case, add actual API credentials to a [`locales.yml`](https://github.com/hakanensari/vacuum/blob/master/test/locales.yml.example) file under `test`.
132
173
 
133
174
  ## Getting Help
134
175
 
@@ -10,6 +10,11 @@ module Vacuum
10
10
  class << self
11
11
  extend Forwardable
12
12
 
13
+ # @!method new
14
+ # Delegates to {Request} to create a new request
15
+ #
16
+ # @return [Request]
17
+ # @see Request#initialize
13
18
  def_delegator 'Vacuum::Request', :new
14
19
  end
15
20
  end
@@ -1,53 +1,64 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vacuum
4
- # The target Amazon locale
5
- # @api private
4
+ # The target locale
5
+ #
6
+ # @see https://webservices.amazon.com/paapi5/documentation/common-request-parameters.html#host-and-region
6
7
  class Locale
7
- class NotFound < ArgumentError; end
8
+ # Raised when the provided marketplace does not correspond to an existing
9
+ # Amazon locale
10
+ class NotFound < KeyError; end
8
11
 
9
- attr_reader :code, :domain, :region
12
+ HOSTS_AND_REGIONS = {
13
+ au: ['webservices.amazon.com.au', 'us-west-2'],
14
+ br: ['webservices.amazon.com.br', 'us-east-1'],
15
+ ca: ['webservices.amazon.ca', 'us-east-1'],
16
+ fr: ['webservices.amazon.fr', 'eu-west-1'],
17
+ de: ['webservices.amazon.de', 'eu-west-1'],
18
+ in: ['webservices.amazon.in', 'eu-west-1'],
19
+ it: ['webservices.amazon.it', 'eu-west-1'],
20
+ jp: ['webservices.amazon.co.jp', 'us-west-2'],
21
+ mx: ['webservices.amazon.com.mx', 'us-east-1'],
22
+ nl: ['webservices.amazon.nl', 'eu-west-1'],
23
+ sg: ['webservices.amazon.sg', 'us-west-2'],
24
+ es: ['webservices.amazon.es', 'eu-west-1'],
25
+ tr: ['webservices.amazon.com.tr', 'eu-west-1'],
26
+ ae: ['webservices.amazon.ae', 'eu-west-1'],
27
+ gb: ['webservices.amazon.co.uk', 'eu-west-1'],
28
+ us: ['webservices.amazon.com', 'us-east-1']
29
+ }.freeze
30
+ private_constant :HOSTS_AND_REGIONS
10
31
 
11
- def self.find(code)
12
- code = code.to_sym.downcase
13
- code = :gb if code == :uk
32
+ # @return [String]
33
+ attr_reader :host, :region, :access_key, :secret_key, :partner_tag,
34
+ :partner_type
14
35
 
15
- @all.find { |locale| locale.code == code } || raise(NotFound)
36
+ # Creates a locale
37
+ #
38
+ # @param [Symbol,String] marketplace
39
+ # @param [String] access_key
40
+ # @param [String] secret_key
41
+ # @param [String] partner_tag
42
+ # @param [String] partner_type
43
+ # @raise [NotFound] if marketplace is not found
44
+ def initialize(marketplace, access_key:, secret_key:, partner_tag:,
45
+ partner_type: 'Associates')
46
+ @host, @region = find_host_and_region(marketplace)
47
+ @access_key = access_key
48
+ @secret_key = secret_key
49
+ @partner_tag = partner_tag
50
+ @partner_type = partner_type
16
51
  end
17
52
 
18
- def initialize(code, domain, region)
19
- @code = code
20
- @domain = domain
21
- @region = region
22
- end
53
+ private
23
54
 
24
- def endpoint
25
- "webservices.#{domain}"
26
- end
55
+ def find_host_and_region(marketplace)
56
+ marketplace = marketplace.to_sym.downcase
57
+ marketplace = :gb if marketplace == :uk
27
58
 
28
- def marketplace
29
- "www.#{domain}"
59
+ HOSTS_AND_REGIONS.fetch(marketplace)
60
+ rescue KeyError
61
+ raise NotFound, "marketplace not found: :#{marketplace}"
30
62
  end
31
-
32
- def build_url(operation)
33
- "https://#{endpoint}/paapi5/#{operation.downcase}"
34
- end
35
-
36
- @all = [
37
- [:au, 'amazon.com.au', 'us-west-2'],
38
- [:br, 'amazon.com.br', 'us-east-1'],
39
- [:ca, 'amazon.ca', 'us-east-1'],
40
- [:fr, 'amazon.fr', 'eu-west-1'],
41
- [:de, 'amazon.de', 'eu-west-1'],
42
- [:in, 'amazon.in', 'eu-west-1'],
43
- [:it, 'amazon.it', 'eu-west-1'],
44
- [:jp, 'amazon.co.jp', 'us-west-2'],
45
- [:mx, 'amazon.com.mx', 'us-east-1'],
46
- [:es, 'amazon.es', 'eu-west-1'],
47
- [:tr, 'amazon.com.tr', 'eu-west-1'],
48
- [:ae, 'amazon.ae', 'eu-west-1'],
49
- [:gb, 'amazon.co.uk', 'eu-west-1'],
50
- [:us, 'amazon.com', 'us-east-1']
51
- ].map { |attributes| new(*attributes) }
52
63
  end
53
64
  end
@@ -4,20 +4,35 @@ require 'json'
4
4
 
5
5
  module Vacuum
6
6
  # Custom VCR matcher for stubbing calls to the Product Advertising API
7
- # @api private
7
+ #
8
+ # The matcher is not required by default.
9
+ #
10
+ # @example
11
+ # require 'vacuum/matcher'
12
+ #
13
+ # # in your test
14
+ # VCR.insert_cassette('cassette_name',
15
+ # match_requests_on: [Vacuum::Matcher])
16
+ #
17
+ # @see https://relishapp.com/vcr/vcr/v/5-0-0/docs/request-matching/register-and-use-a-custom-matcher
8
18
  class Matcher
9
19
  IGNORED_KEYS = %w[PartnerTag].freeze
20
+ private_constant :IGNORED_KEYS
10
21
 
22
+ # @!visibility private
11
23
  attr_reader :requests
12
24
 
25
+ # @!visibility private
13
26
  def self.call(*requests)
14
27
  new(*requests).compare
15
28
  end
16
29
 
30
+ # @!visibility private
17
31
  def initialize(*requests)
18
32
  @requests = requests
19
33
  end
20
34
 
35
+ # @!visibility private
21
36
  def compare
22
37
  uris.reduce(:==) && bodies.reduce(:==)
23
38
  end
@@ -38,3 +53,21 @@ module Vacuum
38
53
  end
39
54
  end
40
55
  end
56
+
57
+ # :nocov:
58
+ if defined?(RSpec)
59
+ RSpec.configure do |config|
60
+ config.around do |example|
61
+ if example.metadata[:paapi]
62
+ metadata = example.metadata[:paapi]
63
+ metadata = {} if metadata == true
64
+ example.metadata[:vcr] = metadata.merge(
65
+ match_requests_on: [Vacuum::Matcher]
66
+ )
67
+ end
68
+
69
+ example.run
70
+ end
71
+ end
72
+ end
73
+ # :nocov:
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aws-sigv4'
4
+ require 'http'
5
+ require 'json'
6
+
7
+ module Vacuum
8
+ # An Amazon Product Advertising API operation
9
+ class Operation
10
+ # @!visibility private
11
+ attr_reader :locale, :name, :params
12
+
13
+ # Creates a new operation
14
+ #
15
+ # @param [String] name
16
+ # @param [Hash] params
17
+ # @param [Locale] locale
18
+ def initialize(name, params:, locale:)
19
+ @name = name
20
+ @params = params
21
+ @locale = locale
22
+ end
23
+
24
+ # @return [Hash]
25
+ def headers
26
+ signature.headers.merge(
27
+ 'x-amz-target' =>
28
+ "com.amazon.paapi5.v1.ProductAdvertisingAPIv1.#{name}",
29
+ 'content-encoding' => 'amz-1.0',
30
+ 'content-type' => 'application/json; charset=utf-8'
31
+ )
32
+ end
33
+
34
+ # @return [String]
35
+ def body
36
+ @body ||= build_body
37
+ end
38
+
39
+ # @return [String]
40
+ def url
41
+ @url ||= build_url
42
+ end
43
+
44
+ private
45
+
46
+ def build_body
47
+ hsh = { 'PartnerTag' => locale.partner_tag,
48
+ 'PartnerType' => locale.partner_type }
49
+
50
+ params.each do |key, val|
51
+ key = key.to_s.split('_')
52
+ .map { |word| word == 'asin' ? 'ASIN' : word.capitalize }.join
53
+ hsh[key] = val
54
+ end
55
+
56
+ JSON.generate(hsh)
57
+ end
58
+
59
+ def build_url
60
+ "https://#{locale.host}/paapi5/#{name.downcase}"
61
+ end
62
+
63
+ def signature
64
+ signer.sign_request(http_method: 'POST', url: url, body: body)
65
+ end
66
+
67
+ def signer
68
+ Aws::Sigv4::Signer.new(service: 'ProductAdvertisingAPI',
69
+ region: locale.region,
70
+ access_key_id: locale.access_key,
71
+ secret_access_key: locale.secret_key,
72
+ http_method: 'POST', endpoint: locale.host)
73
+ end
74
+ end
75
+ end
@@ -1,38 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aws-sigv4'
4
- require 'httpi'
5
- require 'json'
3
+ require 'http'
6
4
 
7
5
  require 'vacuum/locale'
6
+ require 'vacuum/operation'
8
7
  require 'vacuum/response'
9
8
 
10
9
  module Vacuum
11
10
  # A request to the Amazon Product Advertising API
12
11
  class Request
13
- SERVICE = 'ProductAdvertisingAPI'
14
- private_constant :SERVICE
12
+ # @return [HTTP::Client]
13
+ attr_reader :client
15
14
 
16
- # @api private
17
- attr_reader :access_key, :secret_key, :locale, :partner_tag, :partner_type
15
+ # @return [Locale]
16
+ attr_reader :locale
17
+
18
+ # @return [Operation]
19
+ attr_reader :operation
18
20
 
19
21
  # Creates a new request
20
- # @param [Symbol,String] marketplace the two-letter country code of the
21
- # target Amazon locale
22
- # @param [String] access_key your access key
23
- # @param [String] secret_key your secret key
24
- # @param [String] partner_tag your partner tag
25
- # @param [String] partner_type your partner type
26
- def initialize(marketplace: :us, access_key:, secret_key:, partner_tag:,
27
- partner_type: 'Associates')
28
- @locale = Locale.find(marketplace)
29
- @access_key = access_key
30
- @secret_key = secret_key
31
- @partner_tag = partner_tag
32
- @partner_type = partner_type
22
+ #
23
+ # @overload initialize(marketplace: :us, access_key:, secret_key:, partner_tag:, partner_type:)
24
+ # @param [Symbol,String] marketplace
25
+ # @param [String] access_key
26
+ # @param [String] secret_key
27
+ # @param [String] partner_tag
28
+ # @param [String] partner_type
29
+ # @raise [Locale::NotFound] if marketplace is not found
30
+ def initialize(marketplace: :us, **args)
31
+ @locale = Locale.new(marketplace, **args)
32
+ @client = HTTP::Client.new
33
33
  end
34
34
 
35
35
  # Returns details about specified browse nodes
36
+ #
36
37
  # @see https://webservices.amazon.com/paapi5/documentation/getbrowsenodes.html
37
38
  # @overload get_browse_nodes(browse_node_ids:, languages_of_preference: nil, marketplace: nil, partner_tag: nil, partner_type: nil, resources: nil)
38
39
  # @param [Array<String,Integer>,String,Integer] browse_node_ids
@@ -48,6 +49,7 @@ module Vacuum
48
49
  end
49
50
 
50
51
  # Returns the attributes of one or more items
52
+ #
51
53
  # @see https://webservices.amazon.com/paapi5/documentation/get-items.html
52
54
  # @overload get_items(condition: nil, currency_of_preference: nil, item_id_type: nil, item_ids:, languages_of_preference: nil, marketplace: nil, merchant: nil, offer_count: nil, partner_tag: nil, partner_type: nil, resources: nil)
53
55
  # @param [String,nil] condition
@@ -69,6 +71,7 @@ module Vacuum
69
71
 
70
72
  # Returns a set of items that are the same product, but differ according to
71
73
  # a consistent theme
74
+ #
72
75
  # @see https://webservices.amazon.com/paapi5/documentation/get-variations.html
73
76
  # @overload get_variations(asin:, condition: nil, currency_of_preference: nil, languages_of_preference: nil, marketplace: nil, merchant: nil, offer_count: nil, partner_tag: nil, partner_type: nil, resources: nil, variation_count: nil, variation_page: nil)
74
77
  # @param [String] asin
@@ -89,6 +92,7 @@ module Vacuum
89
92
  end
90
93
 
91
94
  # Searches for items on Amazon based on a search query
95
+ #
92
96
  # @see https://webservices.amazon.com/paapi5/documentation/search-items.html
93
97
  # @overload search_items(actor: nil, artist: nil, author: nil, availability: nil, brand: nil, browse_node_id: nil, condition: nil, currency_of_preference: nil, delivery_flags: nil, item_count: nil, item_page: nil, keywords: nil, languages_of_preference: nil, marketplace: nil, max_price: nil, merchant: nil, min_price: nil, min_reviews_rating: nil, min_savings_percent: nil, offer_count: nil, partner_tag: nil, partner_type: nil, resources: nil, search_index: nil, sort_by: nil, title: nil)
94
98
  # @param [String,nil] actor
@@ -119,69 +123,51 @@ module Vacuum
119
123
  request('SearchItems', params)
120
124
  end
121
125
 
122
- private
123
-
124
- def request(operation, params)
125
- body = build_body(params)
126
- signature = sign(operation, body)
126
+ # Flags as persistent
127
+ #
128
+ # @param [Integer] timeout
129
+ # @return [self]
130
+ def persistent(timeout: 5)
131
+ host = "https://#{locale.host}"
132
+ @client = client.persistent(host, timeout: timeout)
127
133
 
128
- request = HTTPI::Request.new(
129
- headers: request_headers(operation, signature),
130
- url: locale.build_url(operation),
131
- body: body
132
- )
133
-
134
- Response.new(HTTPI.post(request))
134
+ self
135
135
  end
136
136
 
137
- def sign(operation, body)
138
- signer.sign_request(
139
- http_method: 'POST',
140
- url: locale.build_url(operation),
141
- headers: headers(operation),
142
- body: body
143
- )
137
+ # @!method use(*features)
138
+ # Turn on {https://github.com/httprb/http HTTP} features
139
+ #
140
+ # @param features
141
+ # @return [self]
142
+ #
143
+ # @!method via(*proxy)
144
+ # Make a request through an HTTP proxy
145
+ #
146
+ # @param [Array] proxy
147
+ # @raise [HTTP::Request::Error] if HTTP proxy is invalid
148
+ # @return [self]
149
+ %i[timeout via through headers use].each do |method_name|
150
+ define_method(method_name) do |*args, &block|
151
+ @client = client.send(method_name, *args, &block)
152
+ end
144
153
  end
145
154
 
146
- def request_headers(operation, signature)
147
- headers(operation).merge(
148
- 'Content-Type' => 'application/json; charset=utf-8',
149
- 'Authorization' => signature.headers['authorization'],
150
- 'X-Amz-Content-Sha256' => signature.headers['x-amz-content-sha256'],
151
- 'X-Amz-Date' => signature.headers['x-amz-date'],
152
- 'Host' => locale.endpoint
153
- )
154
- end
155
+ private
155
156
 
156
- def headers(operation)
157
- {
158
- 'X-Amz-Target' => "com.amazon.paapi5.v1.#{SERVICE}v1.#{operation}",
159
- 'Content-Encoding' => 'amz-1.0'
160
- }
161
- end
157
+ def validate_keywords(params)
158
+ return unless params[:keywords]
159
+ return if params[:keywords].is_a?(String)
162
160
 
163
- def signer
164
- Aws::Sigv4::Signer.new(
165
- service: SERVICE,
166
- region: locale.region,
167
- access_key_id: access_key,
168
- secret_access_key: secret_key,
169
- http_method: 'POST',
170
- endpoint: locale.endpoint
171
- )
161
+ raise ArgumentError, ':keyword argument expects a String'
172
162
  end
173
163
 
174
- def build_body(params)
175
- hsh = { 'PartnerTag' => partner_tag,
176
- 'PartnerType' => partner_type }
177
-
178
- params.each do |key, val|
179
- key = key.to_s.split('_')
180
- .map { |word| word == 'asin' ? 'ASIN' : word.capitalize }.join
181
- hsh[key] = val
182
- end
164
+ def request(operation_name, params)
165
+ validate_keywords(params)
166
+ @operation = Operation.new(operation_name, params: params, locale: locale)
167
+ response = client.headers(operation.headers)
168
+ .post(operation.url, body: operation.body)
183
169
 
184
- JSON.generate(hsh)
170
+ Response.new(response)
185
171
  end
186
172
  end
187
173
  end