vacuum 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +65 -138
- data/lib/vacuum.rb +3 -2
- data/lib/vacuum/locale.rb +53 -0
- data/lib/vacuum/matcher.rb +40 -0
- data/lib/vacuum/request.rb +171 -106
- data/lib/vacuum/response.rb +10 -21
- data/lib/vacuum/version.rb +1 -1
- data/test/cassettes/vacuum.yml +1217 -659
- data/test/integration_helper.rb +53 -0
- data/test/locales.rb +23 -0
- data/test/locales.yml +28 -0
- data/test/locales.yml.example +20 -0
- data/test/vacuum/test_locale.rb +35 -0
- data/test/vacuum/test_request.rb +44 -0
- data/test/vacuum/test_response.rb +22 -0
- metadata +31 -19
- data/test/test_integration.rb +0 -69
- data/test/test_unit.rb +0 -106
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93f97f47ef585594e52efc75ac3824e47992ad0ac6cbd688f05363579c9feff3
|
4
|
+
data.tar.gz: 4d0399b9b897cd955cb3c2739a3b2fcb4e393c6ee092270d21d28dab8ee503fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eac6cca761cd2ccc75e7eb9d626398505c06400116efe5bb765e5112debb0f3b22da3dab840aa1bbde19de752d841bd836029c6c1bb7f3b12738d74bfecd0e3b
|
7
|
+
data.tar.gz: 8571a8e9c4b64b71038bf7713364c8b56bca14bb08abae6743aaf2cea0a976fe1b0025432ad659b300d8d5763deb05bd9ff4200bdfcb80e52de9a2f3d3e8952c
|
data/README.md
CHANGED
@@ -1,211 +1,138 @@
|
|
1
1
|
# Vacuum
|
2
|
-
[![Travis](https://travis-ci.org/hakanensari/vacuum.svg)](https://travis-ci.org/hakanensari/vacuum)
|
3
2
|
|
4
|
-
|
3
|
+
[![CircleCI](https://circleci.com/gh/hakanensari/vacuum/tree/master.svg?style=svg)](https://circleci.com/gh/hakanensari/vacuum/tree/master)
|
4
|
+
|
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.
|
5
6
|
|
6
7
|
![vacuum](http://f.cl.ly/items/2k2X0e2u0G3k1c260D2u/vacuum.png)
|
7
8
|
|
8
9
|
## Usage
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
### Prerequisite
|
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/).
|
13
12
|
|
14
|
-
|
13
|
+
### Getting Started
|
15
14
|
|
16
|
-
|
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.
|
17
16
|
|
18
|
-
Create a request
|
17
|
+
Create a request with your marketplace credentials, passing the two-letter country code of the marketplace.
|
19
18
|
|
20
19
|
```ruby
|
21
|
-
request = Vacuum.new
|
20
|
+
request = Vacuum.new(marketplace: 'US',
|
21
|
+
access_key: '<ACCESS_KEY>',
|
22
|
+
secret_key: '<SECRET_KEY>',
|
23
|
+
partner_tag: '<PARTNER_TAG>')
|
22
24
|
```
|
23
25
|
|
24
|
-
|
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.
|
25
27
|
|
26
28
|
```ruby
|
27
|
-
|
29
|
+
HTTPI.adapter = :http
|
28
30
|
```
|
29
31
|
|
30
|
-
|
32
|
+
### Operations
|
31
33
|
|
32
|
-
|
33
|
-
request.configure(
|
34
|
-
aws_access_key_id: 'key',
|
35
|
-
aws_secret_access_key: 'secret',
|
36
|
-
associate_tag: 'tag'
|
37
|
-
)
|
38
|
-
```
|
34
|
+
#### GetBrowseNodes
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
```sh
|
43
|
-
export AWS_ACCESS_KEY_ID=key
|
44
|
-
export AWS_SECRET_ACCESS_KEY=secret
|
45
|
-
```
|
46
|
-
|
47
|
-
You will still need to set an associate tag:
|
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.
|
48
37
|
|
49
38
|
```ruby
|
50
|
-
request.
|
39
|
+
request.get_browse_nodes(
|
40
|
+
browse_node_ids: ['283155', '3040'],
|
41
|
+
resources: ['BrowseNodes.Ancestor', 'BrowseNodes.Children']
|
42
|
+
)
|
51
43
|
```
|
52
44
|
|
53
|
-
|
45
|
+
#### GetItems
|
54
46
|
|
55
|
-
|
47
|
+
Given an Item identifier, the `GetItems` operation returns the item attributes, based on the resources specified in the request.
|
56
48
|
|
57
49
|
```ruby
|
58
|
-
request.
|
50
|
+
request.get_items(
|
51
|
+
item_ids: ['B0199980K4', 'B000HZD168', 'B01180YUXS', 'B00BKQTA4A'],
|
52
|
+
resources: ['Images.Primary.Small', 'ItemInfo.Title', 'ItemInfo.Features',
|
53
|
+
'Offers.Summaries.HighestPrice' , 'ParentASIN']
|
54
|
+
)
|
59
55
|
```
|
60
56
|
|
61
|
-
|
62
|
-
|
63
|
-
#### Browse Node Lookup
|
57
|
+
#### GetVariations
|
64
58
|
|
65
|
-
|
59
|
+
Given an ASIN, the `GetVariations` operation returns a set of items that are the same product, but differ according to a consistent theme, for example size and color. These items which differ according to a consistent theme are called variations. A variation is a child ASIN. The parent ASIN is an abstraction of the children items. For example, a shirt is a parent ASIN and parent ASINs cannot be sold. A child ASIN would be a blue shirt, size 16, sold by MyApparelStore. This child ASIN is one of potentially many variations. The ways in which variations differ are called dimensions.
|
66
60
|
|
67
61
|
```ruby
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
62
|
+
request.get_variations(
|
63
|
+
asin: 'B00422MCUS',
|
64
|
+
resources: ['ItemInfo.Title', 'VariationSummary.Price.HighestPrice',
|
65
|
+
'VariationSummary.Price.LowestPrice',
|
66
|
+
'VariationSummary.VariationDimension']
|
72
67
|
)
|
73
68
|
```
|
74
69
|
|
75
|
-
####
|
70
|
+
#### SearchItems
|
76
71
|
|
77
|
-
The
|
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.
|
78
73
|
|
79
74
|
```ruby
|
80
|
-
|
81
|
-
query: {
|
82
|
-
'HMAC' => 'secret',
|
83
|
-
'Item.1.OfferListingId' => '123',
|
84
|
-
'Item.1.Quantity' => 1
|
85
|
-
}
|
86
|
-
)
|
75
|
+
request.search_items(keywords: 'harry potter')
|
87
76
|
```
|
88
77
|
|
89
|
-
|
90
|
-
|
91
|
-
```ruby
|
92
|
-
response = request.cart_add(
|
93
|
-
query: {
|
94
|
-
'CartId' => '123',
|
95
|
-
'HMAC' => 'secret',
|
96
|
-
'Item.1.OfferListingId' => '123',
|
97
|
-
'Item.1.Quantity' => 1
|
98
|
-
}
|
99
|
-
)
|
100
|
-
```
|
78
|
+
### Response
|
101
79
|
|
102
|
-
The
|
80
|
+
The quick and dirty way to consume a response is to parse into a Ruby hash:
|
103
81
|
|
104
82
|
```ruby
|
105
|
-
response
|
106
|
-
query: {
|
107
|
-
'CartId' => '123',
|
108
|
-
'HMAC' => 'secret'
|
109
|
-
}
|
110
|
-
)
|
83
|
+
response.to_h
|
111
84
|
```
|
112
85
|
|
113
|
-
|
86
|
+
You can also `#dig` into the returned Hash:
|
114
87
|
|
115
88
|
```ruby
|
116
|
-
response
|
117
|
-
query: {
|
118
|
-
'CartId' => '123',
|
119
|
-
'HMAC' => 'secret',
|
120
|
-
'CartItemId' => '123'
|
121
|
-
}
|
122
|
-
)
|
89
|
+
response.dig('ItemsResult', 'Items')
|
123
90
|
```
|
124
91
|
|
125
|
-
|
126
|
-
|
127
|
-
The **ItemLookup** operation returns some or all of the attributes of an item, depending on the response group specified in the request. By default, the operation returns an item’s ASIN, manufacturer, product group, and title.
|
92
|
+
You can extend Vacuum with a custom parser. Just swap the original with a class or module that responds to `.parse`.
|
128
93
|
|
129
94
|
```ruby
|
130
|
-
response =
|
131
|
-
|
132
|
-
'ItemId' => '0679753354'
|
133
|
-
}
|
134
|
-
)
|
95
|
+
response.parser = MyParser
|
96
|
+
response.parse
|
135
97
|
```
|
136
98
|
|
137
|
-
|
138
|
-
|
139
|
-
The **ItemSearch** operation returns items that satisfy the search criteria, including one or more search indices.
|
140
|
-
|
141
|
-
```ruby
|
142
|
-
response = request.item_search(
|
143
|
-
query: {
|
144
|
-
'Keywords' => 'Architecture',
|
145
|
-
'SearchIndex' => 'Books'
|
146
|
-
}
|
147
|
-
)
|
148
|
-
```
|
99
|
+
If no custom parser is set, `Vacuum::Response#parse` delegates to `#to_h`.
|
149
100
|
|
150
|
-
|
101
|
+
### VCR Support
|
151
102
|
|
152
|
-
|
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.
|
153
104
|
|
154
105
|
```ruby
|
155
|
-
|
156
|
-
query: {
|
157
|
-
'ItemId' => '0679753354'
|
158
|
-
}
|
159
|
-
)
|
160
|
-
```
|
106
|
+
require 'vacuum/matcher'
|
161
107
|
|
162
|
-
|
108
|
+
VCR.insert_cassette('paapi', match_requests_on: [Vacuum::Matcher])
|
109
|
+
```
|
163
110
|
|
164
|
-
|
111
|
+
## Testing
|
165
112
|
|
166
|
-
|
113
|
+
Tests should pass as-is once you install dependencies.
|
167
114
|
|
168
|
-
```
|
169
|
-
|
170
|
-
query: {
|
171
|
-
'ItemId' => '0679753354'
|
172
|
-
},
|
173
|
-
persistent: true
|
174
|
-
)
|
115
|
+
```sh
|
116
|
+
bundle exec rake
|
175
117
|
```
|
176
118
|
|
177
|
-
|
119
|
+
By default, all requests are stubbed. Use the `RECORD` env var to record new or modified interactions.
|
178
120
|
|
179
|
-
|
180
|
-
|
181
|
-
```ruby
|
182
|
-
response.to_h
|
121
|
+
```sh
|
122
|
+
bundle exec RECORD=true rake
|
183
123
|
```
|
184
124
|
|
185
|
-
You can also
|
125
|
+
You can also run tests against live data.
|
186
126
|
|
187
|
-
```
|
188
|
-
|
127
|
+
```shell
|
128
|
+
bundle exec LIVE=true rake
|
189
129
|
```
|
190
130
|
|
191
|
-
In
|
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.
|
192
132
|
|
193
|
-
|
194
|
-
class MyParser
|
195
|
-
# A parser has to respond to this.
|
196
|
-
def self.parse(body)
|
197
|
-
new(body)
|
198
|
-
end
|
199
|
-
|
200
|
-
def initialize(body)
|
201
|
-
@body = body
|
202
|
-
end
|
133
|
+
## Getting Help
|
203
134
|
|
204
|
-
|
205
|
-
|
135
|
+
* Ask specific questions about the API on the [Amazon forum](https://forums.aws.amazon.com/forum.jspa?forumID=9).
|
136
|
+
* Report bugs and discuss potential features in [GitHub issues](https://github.com/hakanensari/vacuum/issues).
|
206
137
|
|
207
|
-
response.parser = MyParser
|
208
|
-
response.parse
|
209
|
-
```
|
210
138
|
|
211
|
-
If no custom parser is set, `Vacuum::Response#parse` delegates to `#to_h`.
|
data/lib/vacuum.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
|
4
5
|
require 'vacuum/request'
|
5
6
|
require 'vacuum/version'
|
6
7
|
|
7
|
-
#
|
8
|
+
# Ruby wrapper to the Amazon Product Advertising API
|
8
9
|
module Vacuum
|
9
10
|
class << self
|
10
11
|
extend Forwardable
|
11
12
|
|
12
|
-
def_delegator Vacuum::Request, :new
|
13
|
+
def_delegator 'Vacuum::Request', :new
|
13
14
|
end
|
14
15
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vacuum
|
4
|
+
# The target Amazon locale
|
5
|
+
# @api private
|
6
|
+
class Locale
|
7
|
+
class NotFound < ArgumentError; end
|
8
|
+
|
9
|
+
attr_reader :code, :domain, :region
|
10
|
+
|
11
|
+
def self.find(code)
|
12
|
+
code = code.to_sym.downcase
|
13
|
+
code = :gb if code == :uk
|
14
|
+
|
15
|
+
@all.find { |locale| locale.code == code } || raise(NotFound)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(code, domain, region)
|
19
|
+
@code = code
|
20
|
+
@domain = domain
|
21
|
+
@region = region
|
22
|
+
end
|
23
|
+
|
24
|
+
def endpoint
|
25
|
+
"webservices.#{domain}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def marketplace
|
29
|
+
"www.#{domain}"
|
30
|
+
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
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Vacuum
|
6
|
+
# Custom VCR matcher for stubbing calls to the Product Advertising API
|
7
|
+
# @api private
|
8
|
+
class Matcher
|
9
|
+
IGNORED_KEYS = %w[PartnerTag].freeze
|
10
|
+
|
11
|
+
attr_reader :requests
|
12
|
+
|
13
|
+
def self.call(*requests)
|
14
|
+
new(*requests).compare
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(*requests)
|
18
|
+
@requests = requests
|
19
|
+
end
|
20
|
+
|
21
|
+
def compare
|
22
|
+
uris.reduce(:==) && bodies.reduce(:==)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def uris
|
28
|
+
requests.map(&:uri)
|
29
|
+
end
|
30
|
+
|
31
|
+
def bodies
|
32
|
+
requests.map do |req|
|
33
|
+
params = JSON.parse(req.body)
|
34
|
+
IGNORED_KEYS.each { |k| params.delete(k) }
|
35
|
+
|
36
|
+
params
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/vacuum/request.rb
CHANGED
@@ -1,122 +1,187 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'aws-sigv4'
|
4
|
+
require 'httpi'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
require 'vacuum/locale'
|
4
8
|
require 'vacuum/response'
|
5
9
|
|
6
10
|
module Vacuum
|
7
|
-
#
|
11
|
+
# A request to the Amazon Product Advertising API
|
8
12
|
class Request
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
13
|
+
SERVICE = 'ProductAdvertisingAPI'
|
14
|
+
private_constant :SERVICE
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
attr_reader :access_key, :secret_key, :locale, :partner_tag, :partner_type
|
18
|
+
|
19
|
+
# 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
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns details about specified browse nodes
|
36
|
+
# @see https://webservices.amazon.com/paapi5/documentation/getbrowsenodes.html
|
37
|
+
# @overload get_browse_nodes(browse_node_ids:, languages_of_preference: nil, marketplace: nil, partner_tag: nil, partner_type: nil, resources: nil)
|
38
|
+
# @param [Array<String,Integer>,String,Integer] browse_node_ids
|
39
|
+
# @param [Array<String>,nil] languages_of_preference
|
40
|
+
# @param [String,nil] marketplace
|
41
|
+
# @param [String,nil] partner_tag
|
42
|
+
# @param [String,nil] partner_type
|
43
|
+
# @param [Array<String>,nil] resources
|
44
|
+
# @return [Response]
|
45
|
+
def get_browse_nodes(browse_node_ids:, **params)
|
46
|
+
params.update(browse_node_ids: Array(browse_node_ids))
|
47
|
+
request('GetBrowseNodes', params)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the attributes of one or more items
|
51
|
+
# @see https://webservices.amazon.com/paapi5/documentation/get-items.html
|
52
|
+
# @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
|
+
# @param [String,nil] condition
|
54
|
+
# @param [String,nil] currency_of_preference
|
55
|
+
# @param [String,nil] item_id_type
|
56
|
+
# @param [Array<String>,String] item_ids
|
57
|
+
# @param [Array<String>,nil] languages_of_preference
|
58
|
+
# @param [String,nil] marketplace
|
59
|
+
# @param [String,nil] merchant
|
60
|
+
# @param [Integer,nil] offer_count
|
61
|
+
# @param [String,nil] partner_tag
|
62
|
+
# @param [String,nil] partner_type
|
63
|
+
# @param [Array<String>,nil] resources
|
64
|
+
# @return [Response]
|
65
|
+
def get_items(item_ids:, **params)
|
66
|
+
params.update(item_ids: Array(item_ids))
|
67
|
+
request('GetItems', params)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns a set of items that are the same product, but differ according to
|
71
|
+
# a consistent theme
|
72
|
+
# @see https://webservices.amazon.com/paapi5/documentation/get-variations.html
|
73
|
+
# @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
|
+
# @param [String] asin
|
75
|
+
# @param [String,nil] condition
|
76
|
+
# @param [String,nil] currency_of_preference
|
77
|
+
# @param [Array<String>,nil] languages_of_preference
|
78
|
+
# @param [String,nil] marketplace
|
79
|
+
# @param [String,nil] merchant
|
80
|
+
# @param [Integer,nil] offer_count
|
81
|
+
# @param [String,nil] partner_tag
|
82
|
+
# @param [String,nil] partner_type
|
83
|
+
# @param [Array<String>,nil] resources
|
84
|
+
# @param [Integer,nil] variation_count
|
85
|
+
# @param [Integer,nil] variation_page
|
86
|
+
# @return [Response]
|
87
|
+
def get_variations(**params)
|
88
|
+
request('GetVariations', params)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Searches for items on Amazon based on a search query
|
92
|
+
# @see https://webservices.amazon.com/paapi5/documentation/search-items.html
|
93
|
+
# @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
|
+
# @param [String,nil] actor
|
95
|
+
# @param [String,nil] artist
|
96
|
+
# @param [String,nil] availability
|
97
|
+
# @param [String,nil] brand
|
98
|
+
# @param [Integer,nil] browse_node_id
|
99
|
+
# @param [String,nil] condition
|
100
|
+
# @param [String,nil] currency_of_preference
|
101
|
+
# @param [Array<String>,nil] delivery_flags
|
102
|
+
# @param [Integer,nil] item_count
|
103
|
+
# @param [Integer,nil] item_page
|
104
|
+
# @param [String,nil] keywords
|
105
|
+
# @param [Array<String>,nil] languages_of_preference
|
106
|
+
# @param [Integer,nil] max_price
|
107
|
+
# @param [String,nil] merchant
|
108
|
+
# @param [Integer,nil] min_price
|
109
|
+
# @param [Integer,nil] min_reviews_rating
|
110
|
+
# @param [Integer,nil] min_savings_percent
|
111
|
+
# @param [Integer,nil] offer_count
|
112
|
+
# @param [Hash,nil] properties
|
113
|
+
# @param [Array<String>,nil] resources
|
114
|
+
# @param [String,nil] search_index
|
115
|
+
# @param [String,nil] sort_by
|
116
|
+
# @param [String,nil] title
|
117
|
+
# @return [Response]
|
118
|
+
def search_items(**params)
|
119
|
+
request('SearchItems', params)
|
64
120
|
end
|
65
121
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
122
|
+
private
|
123
|
+
|
124
|
+
def request(operation, params)
|
125
|
+
body = build_body(params)
|
126
|
+
signature = sign(operation, body)
|
127
|
+
|
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))
|
79
135
|
end
|
80
136
|
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
+
)
|
84
144
|
end
|
85
145
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
+
|
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
|
162
|
+
|
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
|
+
)
|
172
|
+
end
|
173
|
+
|
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
|
119
182
|
end
|
183
|
+
|
184
|
+
JSON.generate(hsh)
|
120
185
|
end
|
121
186
|
end
|
122
187
|
end
|