vacuum 4.3.0 → 5.0.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.
- checksums.yaml +4 -4
- data/README.md +65 -100
- data/lib/vacuum/client.rb +145 -0
- data/lib/vacuum/version.rb +1 -1
- data/lib/vacuum.rb +8 -8
- metadata +8 -32
- data/lib/vacuum/locale.rb +0 -70
- data/lib/vacuum/matcher.rb +0 -73
- data/lib/vacuum/operation.rb +0 -75
- data/lib/vacuum/request.rb +0 -173
- data/lib/vacuum/response.rb +0 -48
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc789e68874474859f4ca103b08ce6c56e7d714eafa76f1f2780efabc232726f
|
|
4
|
+
data.tar.gz: 48d6f52b193a912da0ed88845813d6814786ea60fc83ceff2541407c482e4097
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a58ce7a92532ac352ebef589caf42192a958af8203fdf4eae1e0d859b317b13afc8f9b79b635645b92d4bdb756eb9f21e4505ca5c2c091ab1235b32a2dc2dec
|
|
7
|
+
data.tar.gz: 538c6c125cf2d3b22622df7ff0ac91ffa76536edcf81ab3d8849968046f016926e64da300301a53fa5757125d16d96dbb67180539b62dc3476de8a248a0efec7
|
data/README.md
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
# Vacuum
|
|
2
2
|
|
|
3
|
-
[
|
|
4
|
-
[](https://codeclimate.com/github/hakanensari/vacuum/maintainability)
|
|
5
|
-
[](https://codeclimate.com/github/hakanensari/vacuum/test_coverage)
|
|
6
|
-
|
|
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.
|
|
3
|
+
Vacuum is a Ruby wrapper to the [Amazon Creators API](https://affiliate-program.amazon.com/creatorsapi/docs/en-us/). The API provides programmatic access to product information on the Amazon marketplaces.
|
|
12
4
|
|
|
13
5
|

|
|
14
6
|
|
|
@@ -16,40 +8,53 @@ You need to [register first](https://webservices.amazon.com/paapi5/documentation
|
|
|
16
8
|
|
|
17
9
|
### Getting Started
|
|
18
10
|
|
|
19
|
-
Create a
|
|
11
|
+
Create a client with your credentials.
|
|
20
12
|
|
|
21
13
|
```ruby
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
client = Vacuum.new(
|
|
15
|
+
credential_id: "YOUR_CREDENTIAL_ID",
|
|
16
|
+
credential_secret: "YOUR_CREDENTIAL_SECRET",
|
|
17
|
+
version: "2.1"
|
|
18
|
+
)
|
|
26
19
|
```
|
|
27
20
|
|
|
21
|
+
The client handles authentication automatically and caches access tokens for one hour.
|
|
22
|
+
|
|
28
23
|
You can now access the API using the available operations.
|
|
29
24
|
|
|
30
25
|
```ruby
|
|
31
|
-
response =
|
|
32
|
-
|
|
26
|
+
response = client.search_items(
|
|
27
|
+
marketplace: "www.amazon.com",
|
|
28
|
+
partner_tag: "yourtag-20",
|
|
29
|
+
keywords: "lean startup"
|
|
30
|
+
)
|
|
31
|
+
response.parse
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
### Versions and Marketplaces
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
The `version` determines which authentication endpoint to use:
|
|
37
|
+
|
|
38
|
+
| Version | Region |
|
|
39
|
+
|---------|--------|
|
|
40
|
+
| `"2.1"` | North America |
|
|
41
|
+
| `"2.2"` | Europe |
|
|
42
|
+
| `"2.3"` | Far East |
|
|
40
43
|
|
|
41
44
|
### Operations
|
|
42
45
|
|
|
43
|
-
|
|
46
|
+
Each operation requires `marketplace` (e.g., `"www.amazon.com"`) and `partner_tag` parameters.
|
|
44
47
|
|
|
45
48
|
#### GetBrowseNodes
|
|
46
49
|
|
|
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.
|
|
50
|
+
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.
|
|
48
51
|
|
|
49
52
|
```ruby
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
client.get_browse_nodes(
|
|
54
|
+
marketplace: "www.amazon.com",
|
|
55
|
+
partner_tag: "yourtag-20",
|
|
56
|
+
browse_node_ids: ["283155", "3040"],
|
|
57
|
+
resources: ["browseNodes.ancestor", "browseNodes.children"]
|
|
53
58
|
)
|
|
54
59
|
```
|
|
55
60
|
|
|
@@ -58,23 +63,27 @@ request.get_browse_nodes(
|
|
|
58
63
|
Given an Item identifier, the `GetItems` operation returns the item attributes, based on the resources specified in the request.
|
|
59
64
|
|
|
60
65
|
```ruby
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
client.get_items(
|
|
67
|
+
marketplace: "www.amazon.com",
|
|
68
|
+
partner_tag: "yourtag-20",
|
|
69
|
+
item_ids: ["B0199980K4", "B000HZD168"],
|
|
70
|
+
resources: ["images.primary.small", "itemInfo.title", "itemInfo.features",
|
|
71
|
+
"offersV2.listings.price", "parentASIN"]
|
|
65
72
|
)
|
|
66
73
|
```
|
|
67
74
|
|
|
68
75
|
#### GetVariations
|
|
69
76
|
|
|
70
|
-
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.
|
|
77
|
+
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.
|
|
71
78
|
|
|
72
79
|
```ruby
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
client.get_variations(
|
|
81
|
+
marketplace: "www.amazon.com",
|
|
82
|
+
partner_tag: "yourtag-20",
|
|
83
|
+
asin: "B00422MCUS",
|
|
84
|
+
resources: ["itemInfo.title", "variationSummary.price.highestPrice",
|
|
85
|
+
"variationSummary.price.lowestPrice",
|
|
86
|
+
"variationSummary.variationDimension"]
|
|
78
87
|
)
|
|
79
88
|
```
|
|
80
89
|
|
|
@@ -83,65 +92,35 @@ request.get_variations(
|
|
|
83
92
|
The `SearchItems` operation searches for items on Amazon based on a search query. The API returns up to ten items per search request.
|
|
84
93
|
|
|
85
94
|
```ruby
|
|
86
|
-
|
|
95
|
+
client.search_items(
|
|
96
|
+
marketplace: "www.amazon.com",
|
|
97
|
+
partner_tag: "yourtag-20",
|
|
98
|
+
keywords: "harry potter"
|
|
99
|
+
)
|
|
87
100
|
```
|
|
88
101
|
|
|
89
102
|
### Response
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
```ruby
|
|
94
|
-
response.to_h
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
You can also `#dig` into this hash.
|
|
98
|
-
|
|
99
|
-
```ruby
|
|
100
|
-
response.dig('ItemsResult', 'Items')
|
|
101
|
-
```
|
|
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
|
|
115
|
-
You can extend Vacuum with a custom parser. Just swap the original with a class or module that responds to `.parse`.
|
|
104
|
+
Parse the response body into a hash.
|
|
116
105
|
|
|
117
106
|
```ruby
|
|
118
|
-
response.
|
|
119
|
-
response.parse
|
|
107
|
+
response.parse.dig("searchResult", "items")
|
|
120
108
|
```
|
|
121
109
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### VCR
|
|
110
|
+
### Shared Token Cache
|
|
125
111
|
|
|
126
|
-
|
|
112
|
+
By default, each client instance caches its own access token. In multi-process environments like Rails, you can share the token across processes by providing a cache store.
|
|
127
113
|
|
|
128
114
|
```ruby
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
115
|
+
client = Vacuum.new(
|
|
116
|
+
credential_id: "YOUR_CREDENTIAL_ID",
|
|
117
|
+
credential_secret: "YOUR_CREDENTIAL_SECRET",
|
|
118
|
+
version: "2.1",
|
|
119
|
+
cache: Rails.cache
|
|
120
|
+
)
|
|
134
121
|
```
|
|
135
122
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```ruby
|
|
139
|
-
require 'vacuum/matcher'
|
|
140
|
-
|
|
141
|
-
# in your test
|
|
142
|
-
it 'queries Amazon', :paapi do
|
|
143
|
-
end
|
|
144
|
-
```
|
|
123
|
+
The cache must respond to `fetch(key, expires_in:, &block)`.
|
|
145
124
|
|
|
146
125
|
## Development
|
|
147
126
|
|
|
@@ -151,30 +130,16 @@ Clone the repo and install dependencies.
|
|
|
151
130
|
bundle install
|
|
152
131
|
```
|
|
153
132
|
|
|
154
|
-
|
|
133
|
+
Run tests and Rubocop.
|
|
155
134
|
|
|
156
135
|
```sh
|
|
157
136
|
bundle exec rake
|
|
158
137
|
```
|
|
159
138
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```sh
|
|
163
|
-
RECORD=true bundle exec rake test
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
You can set the `LIVE` env var to run all tests against live data.
|
|
167
|
-
|
|
168
|
-
```sh
|
|
169
|
-
LIVE=true bundle exec rake test
|
|
170
|
-
```
|
|
171
|
-
|
|
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`.
|
|
173
|
-
|
|
174
|
-
## Getting Help
|
|
139
|
+
To run API tests, copy `spec/credentials.yml.example` to `spec/credentials.yml` and fill in your credentials.
|
|
175
140
|
|
|
176
|
-
|
|
177
|
-
* Report bugs in [GitHub issues](https://github.com/hakanensari/vacuum/issues).
|
|
178
|
-
* Discuss potential features in [GitHub Discussions](https://github.com/hakanensari/vacuum/discussions).
|
|
141
|
+
---
|
|
179
142
|
|
|
143
|
+
> Amazon is a $250 billion dollar company that reacts to you buying a vacuum by going THIS GUY LOVES BUYING VACUUMS HERE ARE SOME MORE VACUUMS
|
|
180
144
|
|
|
145
|
+
— [@kibblesmith](https://web.archive.org/web/2017/https://twitter.com/kibblesmith/status/724817086309142529)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "http"
|
|
4
|
+
|
|
5
|
+
module Vacuum
|
|
6
|
+
# A client for the Amazon Creators API
|
|
7
|
+
#
|
|
8
|
+
# Handles authentication automatically and caches access tokens.
|
|
9
|
+
#
|
|
10
|
+
# @example Basic usage
|
|
11
|
+
# client = Vacuum::Client.new(
|
|
12
|
+
# credential_id: "YOUR_CREDENTIAL_ID",
|
|
13
|
+
# credential_secret: "YOUR_CREDENTIAL_SECRET",
|
|
14
|
+
# version: "2.1"
|
|
15
|
+
# )
|
|
16
|
+
# response = client.search_items(
|
|
17
|
+
# marketplace: "www.amazon.com",
|
|
18
|
+
# partner_tag: "yourtag-20",
|
|
19
|
+
# keywords: "ruby programming"
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# @example With shared cache (for multi-process environments like Rails)
|
|
23
|
+
# client = Vacuum::Client.new(
|
|
24
|
+
# credential_id: "YOUR_CREDENTIAL_ID",
|
|
25
|
+
# credential_secret: "YOUR_CREDENTIAL_SECRET",
|
|
26
|
+
# version: "2.1",
|
|
27
|
+
# cache: Rails.cache
|
|
28
|
+
# )
|
|
29
|
+
class Client
|
|
30
|
+
AUTH_URLS = {
|
|
31
|
+
"2.1" => "https://creatorsapi.auth.us-east-1.amazoncognito.com/oauth2/token",
|
|
32
|
+
"2.2" => "https://creatorsapi.auth.eu-south-2.amazoncognito.com/oauth2/token",
|
|
33
|
+
"2.3" => "https://creatorsapi.auth.us-west-2.amazoncognito.com/oauth2/token",
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
API_URL = "https://creatorsapi.amazon/catalog/v1"
|
|
37
|
+
|
|
38
|
+
TOKEN_TTL = 3570 # 59.5 minutes (tokens valid for 1 hour)
|
|
39
|
+
|
|
40
|
+
# Creates a new client
|
|
41
|
+
#
|
|
42
|
+
# @param credential_id [String] Your Creators API credential ID
|
|
43
|
+
# @param credential_secret [String] Your Creators API credential secret
|
|
44
|
+
# @param version [String] API version ("2.1", "2.2", or "2.3")
|
|
45
|
+
# @param cache [#fetch] Optional cache store
|
|
46
|
+
# @param http [HTTP::Client]
|
|
47
|
+
def initialize(version:, credential_id:, credential_secret:, cache: nil, http: HTTP)
|
|
48
|
+
@version = version
|
|
49
|
+
@auth_url = AUTH_URLS.fetch(version) do
|
|
50
|
+
raise ArgumentError, "Unknown version: #{version}"
|
|
51
|
+
end
|
|
52
|
+
@credential_id = credential_id
|
|
53
|
+
@credential_secret = credential_secret
|
|
54
|
+
@cache = cache
|
|
55
|
+
@http = http
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Returns details about specified browse nodes
|
|
59
|
+
#
|
|
60
|
+
# @param marketplace [String] The marketplace (e.g., "www.amazon.com")
|
|
61
|
+
# @param partner_tag [String] Your partner/tracking tag
|
|
62
|
+
# @param browse_node_ids [Array<String>] The browse node IDs to look up
|
|
63
|
+
# @return [HTTP::Response]
|
|
64
|
+
def get_browse_nodes(marketplace:, partner_tag:, browse_node_ids:, **params)
|
|
65
|
+
params[:browse_node_ids] = Array(browse_node_ids)
|
|
66
|
+
request("getBrowseNodes", marketplace:, partner_tag:, **params)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns the attributes of one or more items
|
|
70
|
+
#
|
|
71
|
+
# @param marketplace [String] The marketplace (e.g., "www.amazon.com")
|
|
72
|
+
# @param partner_tag [String] Your partner/tracking tag
|
|
73
|
+
# @param item_ids [Array<String>, String] The item IDs (ASINs) to look up
|
|
74
|
+
# @return [HTTP::Response]
|
|
75
|
+
def get_items(marketplace:, partner_tag:, item_ids:, **params)
|
|
76
|
+
params[:item_ids] = Array(item_ids)
|
|
77
|
+
request("getItems", marketplace:, partner_tag:, **params)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns variations of a product
|
|
81
|
+
#
|
|
82
|
+
# @param marketplace [String] The marketplace (e.g., "www.amazon.com")
|
|
83
|
+
# @param partner_tag [String] Your partner/tracking tag
|
|
84
|
+
# @param asin [String] The ASIN to get variations for
|
|
85
|
+
# @return [HTTP::Response]
|
|
86
|
+
def get_variations(marketplace:, partner_tag:, **params)
|
|
87
|
+
request("getVariations", marketplace:, partner_tag:, **params)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Searches for items on Amazon
|
|
91
|
+
#
|
|
92
|
+
# @param marketplace [String] The marketplace (e.g., "www.amazon.com")
|
|
93
|
+
# @param partner_tag [String] Your partner/tracking tag
|
|
94
|
+
# @param keywords [String] Search keywords
|
|
95
|
+
# @return [HTTP::Response]
|
|
96
|
+
def search_items(marketplace:, partner_tag:, **params)
|
|
97
|
+
request("searchItems", marketplace:, partner_tag:, **params)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def request(operation, marketplace:, partner_tag:, **params)
|
|
103
|
+
@http
|
|
104
|
+
.headers(
|
|
105
|
+
"Authorization" => "Bearer #{access_token}, Version #{@version}",
|
|
106
|
+
"x-marketplace" => marketplace,
|
|
107
|
+
)
|
|
108
|
+
.post("#{API_URL}/#{operation}", json: camelize_keys(marketplace:, partner_tag:, **params))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def camelize_keys(**params)
|
|
112
|
+
params.transform_keys { |key| key.to_s.gsub(/_([a-z])/) { Regexp.last_match(1).upcase } }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def access_token
|
|
116
|
+
if @cache
|
|
117
|
+
@cache.fetch(cache_key, expires_in: TOKEN_TTL) { fetch_token }
|
|
118
|
+
else
|
|
119
|
+
@access_token = fetch_token if token_expired?
|
|
120
|
+
@access_token
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def cache_key
|
|
125
|
+
"vacuum/#{@credential_id}/token"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def token_expired?
|
|
129
|
+
@access_token.nil? || Time.now >= @token_expires_at
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def fetch_token
|
|
133
|
+
response = @http
|
|
134
|
+
.basic_auth(user: @credential_id, pass: @credential_secret)
|
|
135
|
+
.post(@auth_url, form: {
|
|
136
|
+
grant_type: "client_credentials",
|
|
137
|
+
scope: "creatorsapi/default",
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
data = response.parse
|
|
141
|
+
@token_expires_at = Time.now + TOKEN_TTL
|
|
142
|
+
@access_token = data["access_token"]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
data/lib/vacuum/version.rb
CHANGED
data/lib/vacuum.rb
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "forwardable"
|
|
4
4
|
|
|
5
|
-
require
|
|
6
|
-
require
|
|
5
|
+
require "vacuum/client"
|
|
6
|
+
require "vacuum/version"
|
|
7
7
|
|
|
8
|
-
# Ruby wrapper to the Amazon
|
|
8
|
+
# Ruby wrapper to the Amazon Creators API
|
|
9
9
|
module Vacuum
|
|
10
10
|
class << self
|
|
11
11
|
extend Forwardable
|
|
12
12
|
|
|
13
13
|
# @!method new
|
|
14
|
-
# Delegates to {
|
|
14
|
+
# Delegates to {Client} to create a new client
|
|
15
15
|
#
|
|
16
|
-
# @return [
|
|
17
|
-
# @see
|
|
18
|
-
def_delegator
|
|
16
|
+
# @return [Client]
|
|
17
|
+
# @see Client#initialize
|
|
18
|
+
def_delegator "Vacuum::Client", :new
|
|
19
19
|
end
|
|
20
20
|
end
|
metadata
CHANGED
|
@@ -1,50 +1,30 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vacuum
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hakan Ensari
|
|
8
8
|
- Stanislav Katkov
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: aws-sigv4
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - "~>"
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.0'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - "~>"
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.0'
|
|
27
13
|
- !ruby/object:Gem::Dependency
|
|
28
14
|
name: http
|
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
|
30
16
|
requirements:
|
|
31
17
|
- - ">="
|
|
32
18
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
34
|
-
- - "<"
|
|
35
|
-
- !ruby/object:Gem::Version
|
|
36
|
-
version: '6.0'
|
|
19
|
+
version: '5.0'
|
|
37
20
|
type: :runtime
|
|
38
21
|
prerelease: false
|
|
39
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
40
23
|
requirements:
|
|
41
24
|
- - ">="
|
|
42
25
|
- !ruby/object:Gem::Version
|
|
43
|
-
version: '
|
|
44
|
-
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '6.0'
|
|
47
|
-
description: A wrapper to the Amazon Product Advertising API
|
|
26
|
+
version: '5.0'
|
|
27
|
+
description: A wrapper to the Amazon Creators API
|
|
48
28
|
email:
|
|
49
29
|
- hakanensari@gmail.com
|
|
50
30
|
- sk@skylup.com
|
|
@@ -55,11 +35,7 @@ files:
|
|
|
55
35
|
- LICENSE
|
|
56
36
|
- README.md
|
|
57
37
|
- lib/vacuum.rb
|
|
58
|
-
- lib/vacuum/
|
|
59
|
-
- lib/vacuum/matcher.rb
|
|
60
|
-
- lib/vacuum/operation.rb
|
|
61
|
-
- lib/vacuum/request.rb
|
|
62
|
-
- lib/vacuum/response.rb
|
|
38
|
+
- lib/vacuum/client.rb
|
|
63
39
|
- lib/vacuum/version.rb
|
|
64
40
|
homepage: https://github.com/hakanensari/vacuum
|
|
65
41
|
licenses:
|
|
@@ -80,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
56
|
- !ruby/object:Gem::Version
|
|
81
57
|
version: '0'
|
|
82
58
|
requirements: []
|
|
83
|
-
rubygems_version:
|
|
59
|
+
rubygems_version: 4.0.3
|
|
84
60
|
specification_version: 4
|
|
85
|
-
summary: Amazon
|
|
61
|
+
summary: Amazon Creators API in Ruby
|
|
86
62
|
test_files: []
|
data/lib/vacuum/locale.rb
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vacuum
|
|
4
|
-
# The target locale
|
|
5
|
-
#
|
|
6
|
-
# @see https://webservices.amazon.com/paapi5/documentation/common-request-parameters.html#host-and-region
|
|
7
|
-
class Locale
|
|
8
|
-
# Raised when the provided marketplace does not correspond to an existing
|
|
9
|
-
# Amazon locale
|
|
10
|
-
class NotFound < KeyError; end
|
|
11
|
-
|
|
12
|
-
HOSTS_AND_REGIONS = {
|
|
13
|
-
au: ['webservices.amazon.com.au', 'us-west-2'],
|
|
14
|
-
be: ['webservices.amazon.be', 'eu-west-1'],
|
|
15
|
-
br: ['webservices.amazon.com.br', 'us-east-1'],
|
|
16
|
-
ca: ['webservices.amazon.ca', 'us-east-1'],
|
|
17
|
-
eg: ['webservices.amazon.eg', 'eu-west-1'],
|
|
18
|
-
fr: ['webservices.amazon.fr', 'eu-west-1'],
|
|
19
|
-
de: ['webservices.amazon.de', 'eu-west-1'],
|
|
20
|
-
in: ['webservices.amazon.in', 'eu-west-1'],
|
|
21
|
-
ie: ['webservices.amazon.ie', 'eu-west-1'],
|
|
22
|
-
it: ['webservices.amazon.it', 'eu-west-1'],
|
|
23
|
-
jp: ['webservices.amazon.co.jp', 'us-west-2'],
|
|
24
|
-
mx: ['webservices.amazon.com.mx', 'us-east-1'],
|
|
25
|
-
nl: ['webservices.amazon.nl', 'eu-west-1'],
|
|
26
|
-
pl: ['webservices.amazon.pl', 'eu-west-1'],
|
|
27
|
-
sg: ['webservices.amazon.sg', 'us-west-2'],
|
|
28
|
-
sa: ['webservices.amazon.sa', 'eu-west-1'],
|
|
29
|
-
es: ['webservices.amazon.es', 'eu-west-1'],
|
|
30
|
-
se: ['webservices.amazon.se', 'eu-west-1'],
|
|
31
|
-
tr: ['webservices.amazon.com.tr', 'eu-west-1'],
|
|
32
|
-
ae: ['webservices.amazon.ae', 'eu-west-1'],
|
|
33
|
-
gb: ['webservices.amazon.co.uk', 'eu-west-1'],
|
|
34
|
-
us: ['webservices.amazon.com', 'us-east-1']
|
|
35
|
-
}.freeze
|
|
36
|
-
private_constant :HOSTS_AND_REGIONS
|
|
37
|
-
|
|
38
|
-
# @return [String]
|
|
39
|
-
attr_reader :host, :region, :access_key, :secret_key, :partner_tag,
|
|
40
|
-
:partner_type
|
|
41
|
-
|
|
42
|
-
# Creates a locale
|
|
43
|
-
#
|
|
44
|
-
# @param [Symbol,String] marketplace
|
|
45
|
-
# @param [String] access_key
|
|
46
|
-
# @param [String] secret_key
|
|
47
|
-
# @param [String] partner_tag
|
|
48
|
-
# @param [String] partner_type
|
|
49
|
-
# @raise [NotFound] if marketplace is not found
|
|
50
|
-
def initialize(marketplace, access_key:, secret_key:, partner_tag:,
|
|
51
|
-
partner_type: 'Associates')
|
|
52
|
-
@host, @region = find_host_and_region(marketplace)
|
|
53
|
-
@access_key = access_key
|
|
54
|
-
@secret_key = secret_key
|
|
55
|
-
@partner_tag = partner_tag
|
|
56
|
-
@partner_type = partner_type
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def find_host_and_region(marketplace)
|
|
62
|
-
marketplace = marketplace.to_sym.downcase
|
|
63
|
-
marketplace = :gb if marketplace == :uk
|
|
64
|
-
|
|
65
|
-
HOSTS_AND_REGIONS.fetch(marketplace)
|
|
66
|
-
rescue KeyError
|
|
67
|
-
raise NotFound, "marketplace not found: :#{marketplace}"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
data/lib/vacuum/matcher.rb
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
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
|
-
#
|
|
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
|
|
18
|
-
class Matcher
|
|
19
|
-
IGNORED_KEYS = %w[PartnerTag].freeze
|
|
20
|
-
private_constant :IGNORED_KEYS
|
|
21
|
-
|
|
22
|
-
# @!visibility private
|
|
23
|
-
attr_reader :requests
|
|
24
|
-
|
|
25
|
-
# @!visibility private
|
|
26
|
-
def self.call(*requests)
|
|
27
|
-
new(*requests).compare
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# @!visibility private
|
|
31
|
-
def initialize(*requests)
|
|
32
|
-
@requests = requests
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# @!visibility private
|
|
36
|
-
def compare
|
|
37
|
-
uris.reduce(:==) && bodies.reduce(:==)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
|
|
42
|
-
def uris
|
|
43
|
-
requests.map(&:uri)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def bodies
|
|
47
|
-
requests.map do |req|
|
|
48
|
-
params = JSON.parse(req.body)
|
|
49
|
-
IGNORED_KEYS.each { |k| params.delete(k) }
|
|
50
|
-
|
|
51
|
-
params
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
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:
|
data/lib/vacuum/operation.rb
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
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:, 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
|
data/lib/vacuum/request.rb
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'http'
|
|
4
|
-
|
|
5
|
-
require 'vacuum/locale'
|
|
6
|
-
require 'vacuum/operation'
|
|
7
|
-
require 'vacuum/response'
|
|
8
|
-
|
|
9
|
-
module Vacuum
|
|
10
|
-
# A request to the Amazon Product Advertising API
|
|
11
|
-
class Request
|
|
12
|
-
# @return [HTTP::Client]
|
|
13
|
-
attr_reader :client
|
|
14
|
-
|
|
15
|
-
# @return [Locale]
|
|
16
|
-
attr_reader :locale
|
|
17
|
-
|
|
18
|
-
# @return [Operation]
|
|
19
|
-
attr_reader :operation
|
|
20
|
-
|
|
21
|
-
# Creates a new request
|
|
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
|
-
end
|
|
34
|
-
|
|
35
|
-
# Returns details about specified browse nodes
|
|
36
|
-
#
|
|
37
|
-
# @see https://webservices.amazon.com/paapi5/documentation/getbrowsenodes.html
|
|
38
|
-
# @overload get_browse_nodes(browse_node_ids:, languages_of_preference: nil, marketplace: nil, partner_tag: nil, partner_type: nil, resources: nil)
|
|
39
|
-
# @param [Array<String,Integer>,String,Integer] browse_node_ids
|
|
40
|
-
# @param [Array<String>,nil] languages_of_preference
|
|
41
|
-
# @param [String,nil] marketplace
|
|
42
|
-
# @param [String,nil] partner_tag
|
|
43
|
-
# @param [String,nil] partner_type
|
|
44
|
-
# @param [Array<String>,nil] resources
|
|
45
|
-
# @return [Response]
|
|
46
|
-
def get_browse_nodes(browse_node_ids:, **params)
|
|
47
|
-
params.update(browse_node_ids: Array(browse_node_ids))
|
|
48
|
-
request('GetBrowseNodes', params)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Returns the attributes of one or more items
|
|
52
|
-
#
|
|
53
|
-
# @see https://webservices.amazon.com/paapi5/documentation/get-items.html
|
|
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)
|
|
55
|
-
# @param [String,nil] condition
|
|
56
|
-
# @param [String,nil] currency_of_preference
|
|
57
|
-
# @param [String,nil] item_id_type
|
|
58
|
-
# @param [Array<String>,String] item_ids
|
|
59
|
-
# @param [Array<String>,nil] languages_of_preference
|
|
60
|
-
# @param [String,nil] marketplace
|
|
61
|
-
# @param [String,nil] merchant
|
|
62
|
-
# @param [Integer,nil] offer_count
|
|
63
|
-
# @param [String,nil] partner_tag
|
|
64
|
-
# @param [String,nil] partner_type
|
|
65
|
-
# @param [Array<String>,nil] resources
|
|
66
|
-
# @return [Response]
|
|
67
|
-
def get_items(item_ids:, **params)
|
|
68
|
-
params.update(item_ids: Array(item_ids))
|
|
69
|
-
request('GetItems', params)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Returns a set of items that are the same product, but differ according to
|
|
73
|
-
# a consistent theme
|
|
74
|
-
#
|
|
75
|
-
# @see https://webservices.amazon.com/paapi5/documentation/get-variations.html
|
|
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)
|
|
77
|
-
# @param [String] asin
|
|
78
|
-
# @param [String,nil] condition
|
|
79
|
-
# @param [String,nil] currency_of_preference
|
|
80
|
-
# @param [Array<String>,nil] languages_of_preference
|
|
81
|
-
# @param [String,nil] marketplace
|
|
82
|
-
# @param [String,nil] merchant
|
|
83
|
-
# @param [Integer,nil] offer_count
|
|
84
|
-
# @param [String,nil] partner_tag
|
|
85
|
-
# @param [String,nil] partner_type
|
|
86
|
-
# @param [Array<String>,nil] resources
|
|
87
|
-
# @param [Integer,nil] variation_count
|
|
88
|
-
# @param [Integer,nil] variation_page
|
|
89
|
-
# @return [Response]
|
|
90
|
-
def get_variations(**params)
|
|
91
|
-
request('GetVariations', params)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Searches for items on Amazon based on a search query
|
|
95
|
-
#
|
|
96
|
-
# @see https://webservices.amazon.com/paapi5/documentation/search-items.html
|
|
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)
|
|
98
|
-
# @param [String,nil] actor
|
|
99
|
-
# @param [String,nil] artist
|
|
100
|
-
# @param [String,nil] availability
|
|
101
|
-
# @param [String,nil] brand
|
|
102
|
-
# @param [Integer,nil] browse_node_id
|
|
103
|
-
# @param [String,nil] condition
|
|
104
|
-
# @param [String,nil] currency_of_preference
|
|
105
|
-
# @param [Array<String>,nil] delivery_flags
|
|
106
|
-
# @param [Integer,nil] item_count
|
|
107
|
-
# @param [Integer,nil] item_page
|
|
108
|
-
# @param [String,nil] keywords
|
|
109
|
-
# @param [Array<String>,nil] languages_of_preference
|
|
110
|
-
# @param [Integer,nil] max_price
|
|
111
|
-
# @param [String,nil] merchant
|
|
112
|
-
# @param [Integer,nil] min_price
|
|
113
|
-
# @param [Integer,nil] min_reviews_rating
|
|
114
|
-
# @param [Integer,nil] min_savings_percent
|
|
115
|
-
# @param [Integer,nil] offer_count
|
|
116
|
-
# @param [Hash,nil] properties
|
|
117
|
-
# @param [Array<String>,nil] resources
|
|
118
|
-
# @param [String,nil] search_index
|
|
119
|
-
# @param [String,nil] sort_by
|
|
120
|
-
# @param [String,nil] title
|
|
121
|
-
# @return [Response]
|
|
122
|
-
def search_items(**params)
|
|
123
|
-
request('SearchItems', params)
|
|
124
|
-
end
|
|
125
|
-
|
|
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:)
|
|
133
|
-
|
|
134
|
-
self
|
|
135
|
-
end
|
|
136
|
-
|
|
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
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
private
|
|
156
|
-
|
|
157
|
-
def validate_keywords(params)
|
|
158
|
-
return unless params[:keywords]
|
|
159
|
-
return if params[:keywords].is_a?(String)
|
|
160
|
-
|
|
161
|
-
raise ArgumentError, ':keyword argument expects a String'
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def request(operation_name, params)
|
|
165
|
-
validate_keywords(params)
|
|
166
|
-
@operation = Operation.new(operation_name, params:, locale:)
|
|
167
|
-
response = client.headers(operation.headers)
|
|
168
|
-
.post(operation.url, body: operation.body)
|
|
169
|
-
|
|
170
|
-
Response.new(response)
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|
data/lib/vacuum/response.rb
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'delegate'
|
|
4
|
-
require 'forwardable'
|
|
5
|
-
require 'json'
|
|
6
|
-
|
|
7
|
-
module Vacuum
|
|
8
|
-
# A wrapper around the API response
|
|
9
|
-
class Response < SimpleDelegator
|
|
10
|
-
extend Forwardable
|
|
11
|
-
|
|
12
|
-
# @!method dig(*key)
|
|
13
|
-
# Delegates to the Hash returned by {Response#to_h} to extract a nested
|
|
14
|
-
# value specified by the sequence of keys
|
|
15
|
-
#
|
|
16
|
-
# @param [String] key one or more keys
|
|
17
|
-
# @see https://ruby-doc.org/core/Hash.html#method-i-dig
|
|
18
|
-
def_delegator :to_h, :dig
|
|
19
|
-
|
|
20
|
-
class << self
|
|
21
|
-
# @return [nil,.parse] an optional custom parser
|
|
22
|
-
attr_accessor :parser
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# @return [nil,.parse] an optional custom parser
|
|
26
|
-
attr_writer :parser
|
|
27
|
-
|
|
28
|
-
# @!attribute [r] parser
|
|
29
|
-
# @return [nil,.parse] an optional custom parser
|
|
30
|
-
def parser
|
|
31
|
-
@parser || self.class.parser
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Parses the response body
|
|
35
|
-
#
|
|
36
|
-
# @note Delegates to {#to_h} if no custom parser is set
|
|
37
|
-
def parse
|
|
38
|
-
parser&.parse(body) || to_h
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Casts body to Hash
|
|
42
|
-
#
|
|
43
|
-
# @return [Hash]
|
|
44
|
-
def to_h
|
|
45
|
-
JSON.parse(body)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|