blurb 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +55 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +217 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/blurb.gemspec +41 -0
- data/lib/blurb.rb +46 -0
- data/lib/blurb/base_resource.rb +72 -0
- data/lib/blurb/bid_recommendation.rb +24 -0
- data/lib/blurb/campaign.rb +62 -0
- data/lib/blurb/profile.rb +12 -0
- data/lib/blurb/report.rb +32 -0
- data/lib/blurb/snapshot.rb +20 -0
- data/lib/blurb/suggested_keyword.rb +56 -0
- data/lib/blurb/token.rb +34 -0
- data/lib/blurb/version.rb +3 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 928e005a12bfa4aa4ed10d4f0a2cf15ef22e8153
|
4
|
+
data.tar.gz: 45e8e3dff5107ade531d12940c2dcb814860dc4a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c1298fed2c2ae82d9e1a1b237c442402426ec7b025c14972163b005bd518bd2e7cff14d856ce5639345aee8a243864d73d26abbc5681f16d9251214036f3874
|
7
|
+
data.tar.gz: 0db358d2dbd433cc970ab77d060b6b3164ac9557e955c5c4d8d5ef24991a5f2c25e7630a51c4f7b8243057b8bda349c3c298fc6dc3a883c6485768b529c0908a
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
# specify the version you desire here
|
10
|
+
- image: circleci/ruby:2.3-node-browsers
|
11
|
+
|
12
|
+
# Specify service dependencies here if necessary
|
13
|
+
# CircleCI maintains a library of pre-built images
|
14
|
+
# documented at https://circleci.com/docs/2.0/circleci-images/
|
15
|
+
# - image: circleci/postgres:9.4
|
16
|
+
|
17
|
+
working_directory: ~/repo
|
18
|
+
|
19
|
+
steps:
|
20
|
+
- checkout
|
21
|
+
|
22
|
+
# Download and cache dependencies
|
23
|
+
- restore_cache:
|
24
|
+
keys:
|
25
|
+
- v1-dependencies-{{ checksum "Gemfile.lock" }}
|
26
|
+
# fallback to using the latest cache if no exact match is found
|
27
|
+
- v1-dependencies-
|
28
|
+
|
29
|
+
- run:
|
30
|
+
name: install dependencies
|
31
|
+
command: |
|
32
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
33
|
+
|
34
|
+
- save_cache:
|
35
|
+
paths:
|
36
|
+
- ./vendor/bundle
|
37
|
+
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
|
38
|
+
|
39
|
+
# run tests!
|
40
|
+
- run:
|
41
|
+
name: run tests
|
42
|
+
command: |
|
43
|
+
mkdir /tmp/test-results
|
44
|
+
|
45
|
+
bundle exec rspec --format progress \
|
46
|
+
--format RspecJunitFormatter \
|
47
|
+
--out /tmp/test-results/rspec.xml \
|
48
|
+
--format progress
|
49
|
+
|
50
|
+
# collect reports
|
51
|
+
- store_test_results:
|
52
|
+
path: /tmp/test-results
|
53
|
+
- store_artifacts:
|
54
|
+
path: /tmp/test-results
|
55
|
+
destination: test-results
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at dan@iserve.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 dlbunker
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,217 @@
|
|
1
|
+
# Blurb
|
2
|
+
|
3
|
+
[![CircleCI](https://circleci.com/gh/iserve-products/blurb.svg?style=shield)](https://circleci.com/gh/iserve-products/blurb)
|
4
|
+
|
5
|
+
Blurb is an API wrapper (written in Ruby and packaged as a Gem) for the Amazon Advertising API. The Amazon Ad API lets you tie in programmatically to
|
6
|
+
Amazon's Advertising Service. More info can be found at [Amazon Advertising](https://services.amazon.com/advertising/overview.htm?ld=NSGoogleAS)
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'blurb'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install blurb
|
23
|
+
|
24
|
+
## API Credentials
|
25
|
+
|
26
|
+
Getting setup to make calls to the Amazon Advertising API can unfortunately be a tedious process.
|
27
|
+
You will need to apply for and have a valid Amazon Advertising Account. You can find that info
|
28
|
+
here: [Amazon Advertising](https://services.amazon.com/advertising/overview.htm?ld=NSGoogleAS)
|
29
|
+
|
30
|
+
You may also want to get a copy of the advertising docs and getting started guides which can be found here:
|
31
|
+
|
32
|
+
1. [Amazon Advertising API Getting Started Guide.pdf](https://advertising.amazon.com/downloads/Amazon_Advertising_API_Getting_Started_Guide.pdf)
|
33
|
+
2. [Amazon Advertising API Reference.pdf](https://advertising.amazon.com/downloads/Amazon_Advertising_API_Reference.pdf)
|
34
|
+
|
35
|
+
Once you have an account you will be assigned a "client_id" and a "client_secret".
|
36
|
+
|
37
|
+
Next you will need to obtain an authorization code from the Amazon API. You can do that by making a browser request to the following URL:
|
38
|
+
|
39
|
+
```
|
40
|
+
https://www.amazon.com/ap/oa?client_id=YOUR_LWA_CLIENT_ID &scope=cpc_advertising:campaign_management&response_type=code&redirect_uri=YOUR_RETURN_URL
|
41
|
+
```
|
42
|
+
|
43
|
+
Enter your client ID in the URL and a valid website as the return URL. Amazon will direct you to login. Login with your
|
44
|
+
advertising credentials. You will then be redirected back to your provided URL. NOTICE that you will have an authorization code listed
|
45
|
+
on the return URL. YOU NEED THIS code to now obtain your refresh token.
|
46
|
+
|
47
|
+
## Obtaining Your Refresh Token
|
48
|
+
Once you have your authorization code, you can obtain your first access token and refresh token.
|
49
|
+
Do that by making a curl call to the following:
|
50
|
+
|
51
|
+
```
|
52
|
+
curl \
|
53
|
+
-X POST \
|
54
|
+
-H "Content-Type:application/x-www-form-urlencoded;charset=UTF-8" \
|
55
|
+
--data "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=YOUR_RETURN_URL&client_id=YOUR_CLIENT_ID&client_secret=YOUR_SECRET_KEY" \
|
56
|
+
https://api.amazon.com/auth/o2/token
|
57
|
+
```
|
58
|
+
|
59
|
+
The response should return a JSON payload. Find the "refresh token" and save it's value.
|
60
|
+
You will need this token to make repeated API calls. The refresh token is tied to your account
|
61
|
+
and won't change as long as your account is valid and hasn't changed.
|
62
|
+
|
63
|
+
The Blurb API wrapper will then use the refresh token to make OAUTH calls to get a valid
|
64
|
+
access token and call the API with the appropriate bearer token headers setup.
|
65
|
+
|
66
|
+
## Obtaining Your Profile ID
|
67
|
+
The last piece you need to obtain before you can be on your way to using the API is the profile.
|
68
|
+
The profile scopes you to certain features and you can have multiple profiles setup for your account.
|
69
|
+
|
70
|
+
You can find your profiles by making the following Blurb call.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Blurb.client_id = "<YOUR_CLIENT_ID>"
|
74
|
+
Blurb.client_secret = "<YOUR_CLIENT_SECRET>"
|
75
|
+
Blurb.refresh_token = "<YOUR_REFRESH_TOKEN>"
|
76
|
+
|
77
|
+
Blurb::Profile.list()
|
78
|
+
```
|
79
|
+
|
80
|
+
This will return a JSON payload of profile information for your account. You can then select the profile ID you want to use.
|
81
|
+
|
82
|
+
## Blurb Values Setup
|
83
|
+
Before using the Blurb API wrapper you need to set your API values. Simply do that by initializing the Blurb values to your API values.
|
84
|
+
If you are using Rails, see below.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Blurb.profile_id = "<YOUR_PROFILE_ID>"
|
88
|
+
Blurb.client_id = "<YOUR_CLIENT_ID>"
|
89
|
+
Blurb.client_secret = "<YOUR_CLIENT_SECRET>"
|
90
|
+
Blurb.refresh_token = "<YOUR_REFRESH_TOKEN>"
|
91
|
+
```
|
92
|
+
|
93
|
+
## Rails Integration
|
94
|
+
|
95
|
+
You can setup the Amazon API keys and tokens in an initializer script.
|
96
|
+
Create a file called config/initializers/blurb.rb and setup the following values
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
Blurb.profile_id = "<YOUR_PROFILE_ID>"
|
100
|
+
Blurb.client_id = "<YOUR_CLIENT_ID>"
|
101
|
+
Blurb.client_secret = "<YOUR_CLIENT_SECRET>"
|
102
|
+
Blurb.refresh_token = "<YOUR_REFRESH_TOKEN>"
|
103
|
+
```
|
104
|
+
|
105
|
+
If you don't want to store your API values in your code which gets checked into a public repo, you can utilize ENV variables
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
Blurb.profile_id = ENV["YOUR_PROFILE_ID_VAR"]
|
109
|
+
Blurb.client_id = ENV["YOUR_CLIENT_ID_VAR"]
|
110
|
+
Blurb.client_secret = ENV["YOUR_CLIENT_SECRET_VAR"]
|
111
|
+
Blurb.refresh_token = ENV["YOUR_REFRESH_TOKEN_VAR"]
|
112
|
+
```
|
113
|
+
|
114
|
+
You could also store API values in a persistence store and initialize from there too.
|
115
|
+
|
116
|
+
## API Environments
|
117
|
+
|
118
|
+
By default Blurb will run API calls to the Amazon Advertising API production environment.
|
119
|
+
If you are under development and want to test out the API to see what it can do, you can set
|
120
|
+
the following Blurb property to use the API test environment.
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
Blurb.test_env = true
|
124
|
+
```
|
125
|
+
|
126
|
+
## Usage
|
127
|
+
|
128
|
+
All API calls have been setup as closely as possible to REST Resource calls.
|
129
|
+
All you need to do is find the appropriate resource object and make a method call on it and Blurb will do the rest.
|
130
|
+
|
131
|
+
NOTE: Not all API endpoints are currently supported by Blurb. Over time we will get more
|
132
|
+
endpoints added. In the mean time you are always welcome to submit a pull request.
|
133
|
+
|
134
|
+
### Profiles
|
135
|
+
List account profiles
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
Blurb::Profile.list()
|
139
|
+
```
|
140
|
+
|
141
|
+
### Campaigns
|
142
|
+
List campaigns
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
Blurb::Campaign.list()
|
146
|
+
```
|
147
|
+
|
148
|
+
Get a campaign
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
Blurb::Campaign.retrieve(campaign_id)
|
152
|
+
```
|
153
|
+
|
154
|
+
Create a campaign
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
Blurb::Campaign.create({
|
158
|
+
"name" => "test",
|
159
|
+
"campaignType" => "sponsoredProducts",
|
160
|
+
"state" => "enabled",
|
161
|
+
"dailyBudget" => 10,
|
162
|
+
"startDate" => (Time.now).strftime('%Y%m%d'),
|
163
|
+
"targetingType" => "abc"
|
164
|
+
})
|
165
|
+
```
|
166
|
+
|
167
|
+
### Reports
|
168
|
+
Request a report
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
payload_response = Blurb::Report.create({
|
172
|
+
"recordType" => Blurb::Report::KEYWORDS,
|
173
|
+
"reportDate" => (Time.now - 2592000).strftime('%Y%m%d'),
|
174
|
+
"metrics" => "impressions,clicks"
|
175
|
+
})
|
176
|
+
```
|
177
|
+
|
178
|
+
Report record types are
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
Blurb::Report::KEYWORDS
|
182
|
+
Blurb::Report::CAMPAIGNS
|
183
|
+
Blurb::Report::AD_GROUPS
|
184
|
+
Blurb::Report::PRODUCT_ADS
|
185
|
+
```
|
186
|
+
|
187
|
+
### Suggested Keywords
|
188
|
+
Suggestions by ASIN
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
Blurb::SuggestedKeyword.asin_suggestions({
|
192
|
+
"asinValue" => "B0006HUJJO"
|
193
|
+
})
|
194
|
+
```
|
195
|
+
|
196
|
+
Suggestions for a list of ASIN's
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
Blurb::SuggestedKeyword.bulk_asin_suggestions({
|
200
|
+
"asins" => ["B0006HUJJO","B0042SWOHI"]
|
201
|
+
})
|
202
|
+
```
|
203
|
+
|
204
|
+
## Development
|
205
|
+
|
206
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
207
|
+
|
208
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
209
|
+
|
210
|
+
## Contributing
|
211
|
+
|
212
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/iserve-products/blurb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
213
|
+
|
214
|
+
|
215
|
+
## License
|
216
|
+
|
217
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "blurb"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/blurb.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'blurb/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "blurb"
|
8
|
+
spec.version = Blurb::VERSION
|
9
|
+
spec.authors = ["dlbunker"]
|
10
|
+
spec.email = ["dan@iserve.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Ruby gem for the Amazon Advertising API}
|
13
|
+
spec.description = %q{Amazon released a new Advertising API in 2017. This gem will integrate and allow you to make API calls to Amazon.}
|
14
|
+
spec.homepage = "https://github.com/iserve-products/blurb"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
"public gem pushes."
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
32
|
+
|
33
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
34
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
35
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
36
|
+
spec.add_development_dependency "rspec_junit_formatter", "~> 0.3.0"
|
37
|
+
|
38
|
+
spec.add_runtime_dependency "rest-client", "~> 2.0"
|
39
|
+
spec.add_runtime_dependency "oauth2", "~> 1.4.0"
|
40
|
+
|
41
|
+
end
|
data/lib/blurb.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "oauth2"
|
2
|
+
require "rest-client"
|
3
|
+
|
4
|
+
require "blurb/version"
|
5
|
+
require "blurb/base_resource"
|
6
|
+
|
7
|
+
require "blurb/bid_recommendation"
|
8
|
+
require "blurb/campaign"
|
9
|
+
require "blurb/profile"
|
10
|
+
require "blurb/report"
|
11
|
+
require "blurb/snapshot"
|
12
|
+
require "blurb/suggested_keyword"
|
13
|
+
require "blurb/token"
|
14
|
+
|
15
|
+
module Blurb
|
16
|
+
TOKEN_URL = "https://api.amazon.com"
|
17
|
+
API_URL = "https://advertising-api.amazon.com"
|
18
|
+
TEST_API_URL = "https://advertising-api-test.amazon.com"
|
19
|
+
EU_API_URL = "https://advertising-api-eu.amazon.com"
|
20
|
+
|
21
|
+
def self.client
|
22
|
+
return OAuth2::Client.new(
|
23
|
+
"",
|
24
|
+
"",
|
25
|
+
:site => TOKEN_URL
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# By default this gem will use the production API url unless the test_env module
|
30
|
+
# variable is set to true. Then the test API url will be used
|
31
|
+
def self.active_api_url
|
32
|
+
if test_env
|
33
|
+
return TEST_API_URL
|
34
|
+
end
|
35
|
+
if eu_env
|
36
|
+
return EU_API_URL
|
37
|
+
end
|
38
|
+
|
39
|
+
return API_URL
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
attr_accessor :client_secret, :client_id, :refresh_token, :profile_id, :test_env, :eu_env
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Blurb
|
2
|
+
class BaseResource
|
3
|
+
def self.profile_request(api_path)
|
4
|
+
access_token = Blurb::Token.retrieve()
|
5
|
+
|
6
|
+
request_config = {
|
7
|
+
method: :get,
|
8
|
+
url: "#{Blurb.active_api_url}#{api_path}",
|
9
|
+
headers: {
|
10
|
+
:Authorization => "Bearer #{access_token['access_token']}",
|
11
|
+
"Content-Type" => "application/json"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
resp = RestClient::Request.execute(request_config)
|
16
|
+
return JSON.parse(resp)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.get_request(api_path, opts = {})
|
20
|
+
access_token = Blurb::Token.retrieve()
|
21
|
+
|
22
|
+
url = "#{Blurb.active_api_url}#{api_path}"
|
23
|
+
url = api_path if opts[:full_path]
|
24
|
+
|
25
|
+
headers_hash = {
|
26
|
+
"Authorization" => "Bearer #{access_token['access_token']}",
|
27
|
+
"Content-Type" => "application/json",
|
28
|
+
"Amazon-Advertising-API-Scope" => Blurb.profile_id
|
29
|
+
}
|
30
|
+
|
31
|
+
headers_hash["Content-Encoding"] = "gzip" if opts[:gzip]
|
32
|
+
# headers_hash.delete("Authorization") if opts[:no_token]
|
33
|
+
|
34
|
+
request_config = {
|
35
|
+
method: :get,
|
36
|
+
url: url,
|
37
|
+
headers: headers_hash,
|
38
|
+
max_redirects: 0
|
39
|
+
}
|
40
|
+
|
41
|
+
begin
|
42
|
+
resp = RestClient::Request.execute(request_config)
|
43
|
+
rescue RestClient::ExceptionWithResponse => err
|
44
|
+
# If this happens, then we are downloading a report from the api, so we can simply download the location
|
45
|
+
if err.response.code == 307
|
46
|
+
return RestClient.get(err.response.headers[:location])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
response = JSON.parse(resp) if resp
|
51
|
+
return response
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.post_request(api_path, payload)
|
55
|
+
access_token = Blurb::Token.retrieve()
|
56
|
+
|
57
|
+
request_config = {
|
58
|
+
method: :post,
|
59
|
+
url: "#{Blurb::API_URL}#{api_path}",
|
60
|
+
payload: payload.to_json,
|
61
|
+
headers: {
|
62
|
+
:Authorization => "Bearer #{access_token['access_token']}",
|
63
|
+
"Content-Type" => "application/json",
|
64
|
+
"Amazon-Advertising-API-Scope" => Blurb.profile_id.to_i
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
resp = RestClient::Request.execute(request_config)
|
69
|
+
return JSON.parse(resp)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Blurb
|
2
|
+
class BidRecommendation < BaseResource
|
3
|
+
def self.ad_group_recommendations(params = {}, opts = {})
|
4
|
+
# required argument checks
|
5
|
+
raise ArgumentError.new("params hash must contain an adGroupId") unless params["adGroupId"]
|
6
|
+
|
7
|
+
get_request("/v1/adGroups/#{params["adGroupId"]}/bidRecommendations")
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.keyword_recommendations(params = {}, opts = {})
|
11
|
+
# required argument checks
|
12
|
+
raise ArgumentError.new("params hash must contain an keywordId") unless params["keywordId"]
|
13
|
+
|
14
|
+
get_request("/v1/keywords/#{params["keywordId"]}/bidRecommendations")
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.bulk_keyword_recommendations(params = {}, opts = {})
|
18
|
+
# required argument checks
|
19
|
+
raise ArgumentError.new("params hash must contain an array of keywordIds") unless params["keywordIds"]
|
20
|
+
|
21
|
+
post_request("/v1/keywords/bidRecommendations", params["keywordIds"])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Blurb
|
2
|
+
class Campaign < BaseResource
|
3
|
+
def self.retrieve(campaign_id)
|
4
|
+
get_request("/v1/campaigns/#{campaign_id}")
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.retrieve_extended(campaign_id)
|
8
|
+
get_request("/v1/campaigns/extended/#{campaign_id}")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.list(params = {}, opts = {})
|
12
|
+
get_request("/v1/campaigns?#{setup_url_params(params)}")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.create(params = {}, opts = {})
|
16
|
+
# required argument checks
|
17
|
+
if !params["name"] && !params["campaignType"] && !params["targetingType"] && !params["state"] && !params["dailyBudget"] && !params["startDate"]
|
18
|
+
raise ArgumentError.new("params hash must contain name, campaignType, targetingType, state, dailyBudget and startDate")
|
19
|
+
end
|
20
|
+
|
21
|
+
post_request("/v1/campaigns", [params])
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.create_bulk(campaign_array, opts = {})
|
25
|
+
post_request("/v1/campaigns", campaign_array)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def self.setup_url_params(params)
|
31
|
+
url_params = ""
|
32
|
+
url_params = "startIndex=#{params['startIndex']}" if params['startIndex']
|
33
|
+
|
34
|
+
if params['count']
|
35
|
+
url_params += "&" if url_params.size > 0
|
36
|
+
url_params += "count=#{params['count']}"
|
37
|
+
end
|
38
|
+
|
39
|
+
if params['campaignType']
|
40
|
+
url_params += "&" if url_params.size > 0
|
41
|
+
url_params += "campaignType=#{params['campaignType']}"
|
42
|
+
end
|
43
|
+
|
44
|
+
if params['stateFilter']
|
45
|
+
url_params += "&" if url_params.size > 0
|
46
|
+
url_params += "stateFilter=#{params['stateFilter']}"
|
47
|
+
end
|
48
|
+
|
49
|
+
if params['name']
|
50
|
+
url_params += "&" if url_params.size > 0
|
51
|
+
url_params += "name=#{params['name']}"
|
52
|
+
end
|
53
|
+
|
54
|
+
if params['campaignIdFilter']
|
55
|
+
url_params += "&" if url_params.size > 0
|
56
|
+
url_params += "campaignIdFilter=#{params['campaignIdFilter']}"
|
57
|
+
end
|
58
|
+
|
59
|
+
return url_params
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/blurb/report.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Blurb
|
2
|
+
class Report < BaseResource
|
3
|
+
CAMPAIGNS = "campaigns"
|
4
|
+
AD_GROUPS = "adGroups"
|
5
|
+
KEYWORDS = "keywords"
|
6
|
+
PRODUCT_ADS = "productAds"
|
7
|
+
|
8
|
+
def self.create(params = {}, opts = {})
|
9
|
+
# required argument checks
|
10
|
+
raise ArgumentError.new("params hash must contain a recordType") unless params["recordType"]
|
11
|
+
|
12
|
+
api_params = {
|
13
|
+
"campaignType" => "sponsoredProducts",
|
14
|
+
"reportDate" => params["reportDate"],
|
15
|
+
"metrics" => params["metrics"]
|
16
|
+
}
|
17
|
+
|
18
|
+
api_params["segment"] = params["segment"] if params["segment"]
|
19
|
+
|
20
|
+
post_request("/v1/#{params["recordType"]}/report", api_params)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.status(report_id, opts = {})
|
24
|
+
get_request("/v1/reports/#{report_id}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.download(location, opts = {})
|
28
|
+
opts.merge!({:full_path => true, :gzip => true, :no_token => true})
|
29
|
+
get_request(location, opts)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Blurb
|
2
|
+
class Snapshot < BaseResource
|
3
|
+
CAMPAIGNS = "campaigns"
|
4
|
+
AD_GROUPS = "adGroups"
|
5
|
+
KEYWORDS = "keywords"
|
6
|
+
NEGATIVE_KEYWORDS = "negativeKeywords"
|
7
|
+
CAMPAIGN_NEGATIVE_KEYWORDS = "campaignNegativeKeywords"
|
8
|
+
PRODUCT_ADS = "productAds"
|
9
|
+
|
10
|
+
def self.create(params = {}, opts = {})
|
11
|
+
# required argument checks
|
12
|
+
raise ArgumentError.new("params hash must contain a recordType") unless params["recordType"]
|
13
|
+
|
14
|
+
post_request("/v1/#{params["recordType"]}/snapshot", {
|
15
|
+
"campaignType" => "sponsoredProducts",
|
16
|
+
"stateFilter" => params["stateFilter"]
|
17
|
+
})
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Blurb
|
2
|
+
class SuggestedKeyword < BaseResource
|
3
|
+
def self.ad_group_suggestions(params = {}, opts = {})
|
4
|
+
# required argument checks
|
5
|
+
raise ArgumentError.new("params hash must contain an adGroupId") unless params["adGroupId"]
|
6
|
+
|
7
|
+
get_request("/v1/adGroups/#{params["adGroupId"]}/suggested/keywords?#{setup_url_params(params)}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.ad_group_extended_suggestions(params = {}, opts = {})
|
11
|
+
# required argument checks
|
12
|
+
raise ArgumentError.new("params hash must contain an adGroupId") unless params["adGroupId"]
|
13
|
+
|
14
|
+
get_request("/v1/adGroups/#{params["adGroupId"]}/suggested/keywords/extended?#{setup_url_params(params)}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.asin_suggestions(params = {}, opts = {})
|
18
|
+
# required argument checks
|
19
|
+
raise ArgumentError.new("params hash must contain an asinValue") unless params["asinValue"]
|
20
|
+
|
21
|
+
get_request("/v1/asins/#{params["asinValue"]}/suggested/keywords?#{setup_url_params(params)}")
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.bulk_asin_suggestions(params = {}, opts = {})
|
25
|
+
# required argument checks
|
26
|
+
raise ArgumentError.new("params hash must contain an asins array") unless params["asins"]
|
27
|
+
|
28
|
+
maxNumSuggestions = 100
|
29
|
+
maxNumSuggestions = params["maxNumSuggestions"] if params["maxNumSuggestions"]
|
30
|
+
|
31
|
+
post_request("/v1/asins/suggested/keywords", {
|
32
|
+
"asins" => params["asins"],
|
33
|
+
"maxNumSuggestions" => maxNumSuggestions
|
34
|
+
})
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.setup_url_params(params)
|
40
|
+
url_params = ""
|
41
|
+
url_params = "maxNumSuggestions=#{params['maxNumSuggestions']}" if params['maxNumSuggestions']
|
42
|
+
|
43
|
+
if params['adStateFilter']
|
44
|
+
url_params += "&" if url_params.size > 0
|
45
|
+
url_params += "adStateFilter=#{params['adStateFilter']}"
|
46
|
+
end
|
47
|
+
|
48
|
+
if params['suggestBids']
|
49
|
+
url_params += "&" if url_params.size > 0
|
50
|
+
url_params += "suggestBids=#{params['suggestBids']}"
|
51
|
+
end
|
52
|
+
|
53
|
+
return url_params
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/blurb/token.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module Blurb
|
2
|
+
class Token
|
3
|
+
|
4
|
+
def self.code(auth_code)
|
5
|
+
response = Blurb::client.request(:post, "/auth/o2/token",
|
6
|
+
{
|
7
|
+
body: {
|
8
|
+
grant_type: "authorization_code",
|
9
|
+
client_id: Blurb.client_id,
|
10
|
+
code: auth_code,
|
11
|
+
client_secret: Blurb.client_secret
|
12
|
+
}
|
13
|
+
}
|
14
|
+
)
|
15
|
+
|
16
|
+
return JSON.parse(response.body)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.retrieve(params = {}, opts = {})
|
20
|
+
response = Blurb::client.request(:post, "/auth/o2/token",
|
21
|
+
{
|
22
|
+
body: {
|
23
|
+
grant_type: "refresh_token",
|
24
|
+
client_id: Blurb.client_id,
|
25
|
+
refresh_token: Blurb.refresh_token,
|
26
|
+
client_secret: Blurb.client_secret
|
27
|
+
}
|
28
|
+
}
|
29
|
+
)
|
30
|
+
|
31
|
+
return JSON.parse(response.body)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blurb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- dlbunker
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-10-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec_junit_formatter
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.3.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.3.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rest-client
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: oauth2
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.4.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.4.0
|
97
|
+
description: Amazon released a new Advertising API in 2017. This gem will integrate
|
98
|
+
and allow you to make API calls to Amazon.
|
99
|
+
email:
|
100
|
+
- dan@iserve.com
|
101
|
+
executables: []
|
102
|
+
extensions: []
|
103
|
+
extra_rdoc_files: []
|
104
|
+
files:
|
105
|
+
- ".circleci/config.yml"
|
106
|
+
- ".gitignore"
|
107
|
+
- ".rspec"
|
108
|
+
- ".travis.yml"
|
109
|
+
- CODE_OF_CONDUCT.md
|
110
|
+
- Gemfile
|
111
|
+
- LICENSE.txt
|
112
|
+
- README.md
|
113
|
+
- Rakefile
|
114
|
+
- bin/console
|
115
|
+
- bin/setup
|
116
|
+
- blurb.gemspec
|
117
|
+
- lib/blurb.rb
|
118
|
+
- lib/blurb/base_resource.rb
|
119
|
+
- lib/blurb/bid_recommendation.rb
|
120
|
+
- lib/blurb/campaign.rb
|
121
|
+
- lib/blurb/profile.rb
|
122
|
+
- lib/blurb/report.rb
|
123
|
+
- lib/blurb/snapshot.rb
|
124
|
+
- lib/blurb/suggested_keyword.rb
|
125
|
+
- lib/blurb/token.rb
|
126
|
+
- lib/blurb/version.rb
|
127
|
+
homepage: https://github.com/iserve-products/blurb
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.6.13
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: Ruby gem for the Amazon Advertising API
|
151
|
+
test_files: []
|