blurb 0.3.2 → 0.4.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 +5 -5
- data/.circleci/config.yml +2 -1
- data/.gitignore +1 -0
- data/.travis.yml +2 -2
- data/README.md +39 -37
- data/bin/console +1 -0
- data/blurb.gemspec +8 -5
- data/lib/blurb.rb +19 -25
- data/lib/blurb/account.rb +108 -0
- data/lib/blurb/base_class.rb +8 -0
- data/lib/blurb/campaign_requests.rb +23 -0
- data/lib/blurb/client.rb +12 -0
- data/lib/blurb/profile.rb +140 -6
- data/lib/blurb/{report.rb → report_requests.rb} +50 -64
- data/lib/blurb/request.rb +81 -0
- data/lib/blurb/request_collection.rb +99 -0
- data/lib/blurb/request_collection_with_campaign_type.rb +12 -0
- data/lib/blurb/snapshot_requests.rb +36 -0
- data/lib/blurb/suggested_keyword_requests.rb +61 -0
- metadata +73 -16
- data/lib/blurb/ad_group.rb +0 -40
- data/lib/blurb/base_resource.rb +0 -172
- data/lib/blurb/bid_recommendation.rb +0 -24
- data/lib/blurb/campaign.rb +0 -38
- data/lib/blurb/keyword.rb +0 -51
- data/lib/blurb/snapshot.rb +0 -35
- data/lib/blurb/suggested_keyword.rb +0 -39
- data/lib/blurb/version.rb +0 -3
@@ -1,48 +1,52 @@
|
|
1
|
-
|
2
|
-
class Report < BaseResource
|
3
|
-
CAMPAIGNS = "campaigns"
|
4
|
-
AD_GROUPS = "adGroups"
|
5
|
-
KEYWORDS = "keywords"
|
6
|
-
PRODUCT_ADS = "productAds"
|
7
|
-
ASINS = "asins"
|
8
|
-
TARGETS = "targets"
|
1
|
+
require 'blurb/request_collection_with_campaign_type'
|
9
2
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
class Blurb
|
4
|
+
class ReportRequests < RequestCollectionWithCampaignType
|
5
|
+
def initialize(campaign_type:, base_url:, headers:)
|
6
|
+
@campaign_type = campaign_type
|
7
|
+
@base_url = "#{base_url}/v2/#{@campaign_type}"
|
8
|
+
@headers = headers
|
9
|
+
end
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
def create(
|
12
|
+
record_type:,
|
13
|
+
report_date: Date.today,
|
14
|
+
metrics: nil,
|
15
|
+
segment: nil
|
16
|
+
)
|
17
|
+
# create payload
|
18
|
+
metrics = get_default_metrics(record_type.to_s.underscore.to_sym) if metrics.nil?
|
19
|
+
payload = {
|
20
|
+
metrics: metrics.map{ |m| m.to_s.camelize(:lower) }.join(","),
|
21
|
+
report_date: report_date
|
20
22
|
}
|
23
|
+
payload[:segment] = segment if segment
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
raise ArgumentError.new("ASIN report is not supported for Sponsored Brands") if params["campaignType"] == SPONSORED_BRANDS
|
28
|
-
else
|
29
|
-
request_url = "/v2/#{params["campaignType"]}/#{params["recordType"]}/report"
|
30
|
-
end
|
31
|
-
|
32
|
-
post_request(request_url, api_params)
|
25
|
+
execute_request(
|
26
|
+
api_path: "/#{record_type.to_s.camelize(:lower)}/report",
|
27
|
+
request_type: :post,
|
28
|
+
payload: payload
|
29
|
+
)
|
33
30
|
end
|
34
31
|
|
35
|
-
def
|
36
|
-
|
32
|
+
def retrieve(report_id)
|
33
|
+
execute_request(
|
34
|
+
api_path: "/reports/#{report_id}",
|
35
|
+
request_type: :get,
|
36
|
+
)
|
37
37
|
end
|
38
38
|
|
39
|
-
def download(
|
40
|
-
|
41
|
-
|
39
|
+
def download(report_id)
|
40
|
+
execute_request(
|
41
|
+
api_path: "/reports/#{report_id}/download",
|
42
|
+
request_type: :get,
|
43
|
+
)
|
42
44
|
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
+
private
|
47
|
+
|
48
|
+
def get_default_metrics(record_type)
|
49
|
+
if @campaign_type == CAMPAIGN_TYPE_CODES[:sb]
|
46
50
|
return [
|
47
51
|
"campaignId",
|
48
52
|
"impressions",
|
@@ -52,7 +56,7 @@ module Blurb
|
|
52
56
|
"attributedSales14dSameSKU",
|
53
57
|
"attributedConversions14d",
|
54
58
|
"attributedConversions14dSameSKU"
|
55
|
-
]
|
59
|
+
] if record_type == :campaigns
|
56
60
|
return [
|
57
61
|
"adGroupId",
|
58
62
|
"campaignId",
|
@@ -63,7 +67,7 @@ module Blurb
|
|
63
67
|
"attributedSales14dSameSKU",
|
64
68
|
"attributedConversions14d",
|
65
69
|
"attributedConversions14dSameSKU"
|
66
|
-
]
|
70
|
+
] if record_type == :ad_groups
|
67
71
|
return [
|
68
72
|
"keywordId",
|
69
73
|
"adGroupId",
|
@@ -74,27 +78,9 @@ module Blurb
|
|
74
78
|
"attributedSales14d",
|
75
79
|
"attributedSales14dSameSKU",
|
76
80
|
"attributedConversions14d",
|
77
|
-
"attributedConversions14dSameSKU"
|
78
|
-
]
|
79
|
-
elsif campaign_type ==
|
80
|
-
return [
|
81
|
-
"campaignName",
|
82
|
-
"campaignId",
|
83
|
-
"adGroupId",
|
84
|
-
"adGroupName",
|
85
|
-
"asin",
|
86
|
-
"otherAsin",
|
87
|
-
"sku",
|
88
|
-
"currency",
|
89
|
-
"attributedUnitsOrdered1dOtherSKU",
|
90
|
-
"attributedUnitsOrdered7dOtherSKU",
|
91
|
-
"attributedUnitsOrdered14dOtherSKU",
|
92
|
-
"attributedUnitsOrdered30dOtherSKU",
|
93
|
-
"attributedSales1dOtherSKU",
|
94
|
-
"attributedSales7dOtherSKU",
|
95
|
-
"attributedSales14dOtherSKU",
|
96
|
-
"attributedSales30dOtherSKU"
|
97
|
-
].join(",") if record_type == ASINS
|
81
|
+
"attributedConversions14dSameSKU"
|
82
|
+
] if record_type == :keywords
|
83
|
+
elsif @campaign_type == CAMPAIGN_TYPE_CODES[:sp]
|
98
84
|
return [
|
99
85
|
"campaignName",
|
100
86
|
"campaignId",
|
@@ -123,7 +109,7 @@ module Blurb
|
|
123
109
|
"attributedSales7dSameSKU",
|
124
110
|
"attributedSales14dSameSKU",
|
125
111
|
"attributedSales30dSameSKU"
|
126
|
-
]
|
112
|
+
] if record_type == :campaigns
|
127
113
|
return [
|
128
114
|
"campaignName",
|
129
115
|
"campaignId",
|
@@ -152,7 +138,7 @@ module Blurb
|
|
152
138
|
"attributedSales7dSameSKU",
|
153
139
|
"attributedSales14dSameSKU",
|
154
140
|
"attributedSales30dSameSKU"
|
155
|
-
]
|
141
|
+
] if record_type == :ad_groups
|
156
142
|
return [
|
157
143
|
"campaignName",
|
158
144
|
"campaignId",
|
@@ -182,7 +168,7 @@ module Blurb
|
|
182
168
|
"attributedSales7dSameSKU",
|
183
169
|
"attributedSales14dSameSKU",
|
184
170
|
"attributedSales30dSameSKU"
|
185
|
-
]
|
171
|
+
] if record_type == :keywords
|
186
172
|
return [
|
187
173
|
"campaignName",
|
188
174
|
"campaignId",
|
@@ -214,7 +200,7 @@ module Blurb
|
|
214
200
|
"attributedSales7dSameSKU",
|
215
201
|
"attributedSales14dSameSKU",
|
216
202
|
"attributedSales30dSameSKU"
|
217
|
-
]
|
203
|
+
] if record_type == :product_ads
|
218
204
|
return [
|
219
205
|
"campaignName",
|
220
206
|
"campaignId",
|
@@ -245,7 +231,7 @@ module Blurb
|
|
245
231
|
"attributedSales7dSameSKU",
|
246
232
|
"attributedSales14dSameSKU",
|
247
233
|
"attributedSales30dSameSKU"
|
248
|
-
]
|
234
|
+
] if record_type == :targets
|
249
235
|
return [
|
250
236
|
"campaignName",
|
251
237
|
"campaignId",
|
@@ -277,7 +263,7 @@ module Blurb
|
|
277
263
|
"attributedSales7dSameSKU",
|
278
264
|
"attributedSales14dSameSKU",
|
279
265
|
"attributedSales30dSameSKU"
|
280
|
-
]
|
266
|
+
] if record_type == :portfolios
|
281
267
|
end
|
282
268
|
end
|
283
269
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
require "rest-client"
|
3
|
+
require "blurb/base_class"
|
4
|
+
|
5
|
+
class Blurb
|
6
|
+
class Request < BaseClass
|
7
|
+
|
8
|
+
def initialize(url:, request_type:, payload: nil, headers:, url_params: nil)
|
9
|
+
@url = setup_url(url, url_params)
|
10
|
+
@payload = convert_payload(payload)
|
11
|
+
@headers = headers
|
12
|
+
@request_type = request_type
|
13
|
+
end
|
14
|
+
|
15
|
+
def request_config
|
16
|
+
request_config = {
|
17
|
+
method: @request_type,
|
18
|
+
url: @url,
|
19
|
+
headers: @headers
|
20
|
+
}
|
21
|
+
|
22
|
+
case @request_type
|
23
|
+
when :get
|
24
|
+
request_config[:max_redirects] = 0
|
25
|
+
when :post, :put
|
26
|
+
request_config[:payload] = @payload if @payload
|
27
|
+
end
|
28
|
+
|
29
|
+
return request_config
|
30
|
+
end
|
31
|
+
|
32
|
+
def make_request
|
33
|
+
begin
|
34
|
+
resp = RestClient::Request.execute(request_config())
|
35
|
+
rescue RestClient::ExceptionWithResponse => err
|
36
|
+
# If this happens, then we are downloading a report from the api, so we can simply download the location
|
37
|
+
if err.response.code == 307
|
38
|
+
return RestClient.get(err.response.headers[:location])
|
39
|
+
else
|
40
|
+
return JSON.parse(err.response.body)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
resp = convert_response(resp)
|
44
|
+
return resp
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def setup_url(url, url_params)
|
49
|
+
url += "?#{URI.encode_www_form(camelcase_keys(url_params))}" if url_params
|
50
|
+
return url
|
51
|
+
end
|
52
|
+
|
53
|
+
def convert_payload(payload)
|
54
|
+
return if payload.nil?
|
55
|
+
payload = payload.map{|r| camelcase_keys(r)} if payload.class == Array
|
56
|
+
payload = camelcase_keys(payload) if payload.class == Hash
|
57
|
+
return payload.to_json
|
58
|
+
end
|
59
|
+
|
60
|
+
def convert_response(resp)
|
61
|
+
resp = JSON.parse(resp)
|
62
|
+
resp = resp.map{|r| underscore_keys(r)} if resp.class == Array
|
63
|
+
resp = underscore_keys(resp) if resp.class == Hash
|
64
|
+
#TODO convert to symbols recursively
|
65
|
+
return resp
|
66
|
+
end
|
67
|
+
|
68
|
+
def camelcase_keys(hash)
|
69
|
+
map = hash.map do |key,value|
|
70
|
+
value = value.strftime('%Y%m%d') if value.class == Date || value.class == Time
|
71
|
+
[key.to_s.camelize(:lower), value]
|
72
|
+
end
|
73
|
+
map.to_h
|
74
|
+
end
|
75
|
+
|
76
|
+
def underscore_keys(hash)
|
77
|
+
hash.map{|k,v| [k.underscore.to_sym, v]}.to_h
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'blurb/request'
|
2
|
+
require "blurb/base_class"
|
3
|
+
|
4
|
+
class Blurb
|
5
|
+
class RequestCollection < BaseClass
|
6
|
+
|
7
|
+
def initialize(headers:, base_url:)
|
8
|
+
@base_url = base_url
|
9
|
+
@headers = headers
|
10
|
+
end
|
11
|
+
|
12
|
+
def list(url_params=nil)
|
13
|
+
execute_request(
|
14
|
+
request_type: :get,
|
15
|
+
url_params: url_params
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list_extended(url_params=nil)
|
20
|
+
execute_request(
|
21
|
+
api_path: "/extended",
|
22
|
+
request_type: :get,
|
23
|
+
url_params: url_params
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def retrieve(resource_id)
|
28
|
+
execute_request(
|
29
|
+
api_path: "/#{resource_id}",
|
30
|
+
request_type: :get
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def retrieve_extended(resource_id)
|
35
|
+
execute_request(
|
36
|
+
api_path: "/extended/#{resource_id}",
|
37
|
+
request_type: :get
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def create(**create_params)
|
42
|
+
create_bulk([create_params])
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_bulk(create_array)
|
46
|
+
execute_bulk_request(
|
47
|
+
request_type: :post,
|
48
|
+
payload: create_array,
|
49
|
+
api_limit: 100
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def update(**update_params)
|
54
|
+
update_bulk([update_params])
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_bulk(update_array)
|
58
|
+
execute_bulk_request(
|
59
|
+
request_type: :put,
|
60
|
+
payload: update_array,
|
61
|
+
api_limit: 100
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete(resource_id)
|
66
|
+
execute_request(
|
67
|
+
api_path: "/#{resource_id}",
|
68
|
+
request_type: :delete
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def execute_request(api_path: "", request_type:, payload: nil, url_params: nil)
|
75
|
+
url = "#{@base_url}#{api_path}"
|
76
|
+
|
77
|
+
request = Request.new(
|
78
|
+
url: url,
|
79
|
+
url_params: url_params,
|
80
|
+
request_type: request_type,
|
81
|
+
payload: payload,
|
82
|
+
headers: @headers
|
83
|
+
)
|
84
|
+
|
85
|
+
request.make_request
|
86
|
+
end
|
87
|
+
|
88
|
+
# Split up bulk requests to match the api limit
|
89
|
+
def execute_bulk_request(api_limit:, **execute_request_params)
|
90
|
+
results = []
|
91
|
+
payloads = execute_request_params[:payload].each_slice(api_limit).to_a
|
92
|
+
payloads.each do |p|
|
93
|
+
execute_request_params[:payload] = p
|
94
|
+
results << execute_request(execute_request_params)
|
95
|
+
end
|
96
|
+
results.flatten
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'blurb/request_collection'
|
2
|
+
|
3
|
+
class Blurb
|
4
|
+
class RequestCollectionWithCampaignType < RequestCollection
|
5
|
+
|
6
|
+
def initialize(campaign_type:, resource:, base_url:, headers:)
|
7
|
+
@campaign_type = campaign_type
|
8
|
+
@base_url = "#{base_url}/v2/#{@campaign_type}/#{resource}"
|
9
|
+
@headers = headers
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'blurb/request_collection_with_campaign_type'
|
2
|
+
|
3
|
+
class Blurb
|
4
|
+
class SnapshotRequests < RequestCollectionWithCampaignType
|
5
|
+
def initialize(campaign_type:, base_url:, headers:)
|
6
|
+
@campaign_type = campaign_type
|
7
|
+
@base_url = "#{base_url}/v2/#{@campaign_type}"
|
8
|
+
@headers = headers
|
9
|
+
end
|
10
|
+
|
11
|
+
def create(record_type, state_filter='enabled,paused')
|
12
|
+
execute_request(
|
13
|
+
api_path: "/#{record_type.to_s.camelize(:lower)}/snapshot",
|
14
|
+
request_type: :post,
|
15
|
+
payload: {state_filter: state_filter}
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def retrieve(snapshot_id)
|
20
|
+
execute_request(
|
21
|
+
api_path: "/snapshots/#{snapshot_id}",
|
22
|
+
request_type: :get,
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def download(snapshot_id)
|
27
|
+
download_url = retrieve(snapshot_id)[:location]
|
28
|
+
headers = @headers.dup["Content-Encoding"] = "gzip"
|
29
|
+
Request.new(
|
30
|
+
url: download_url,
|
31
|
+
request_type: :get,
|
32
|
+
headers: @headers
|
33
|
+
).make_request
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'blurb/request'
|
2
|
+
|
3
|
+
class Blurb
|
4
|
+
class SuggestedKeywordRequests
|
5
|
+
def initialize(base_url:, headers:)
|
6
|
+
@base_url = base_url
|
7
|
+
@headers = headers
|
8
|
+
end
|
9
|
+
|
10
|
+
def ad_group_retrieve(ad_group_id)
|
11
|
+
execute_request(
|
12
|
+
api_path: "/adGroups/#{ad_group_id}/suggested/keywords",
|
13
|
+
request_type: :get
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ad_group_retrieve_extended(ad_group_id)
|
18
|
+
execute_request(
|
19
|
+
api_path: "/adGroups/#{ad_group_id}/suggested/keywords/extended",
|
20
|
+
request_type: :get
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def asin_retrieve(asin_value, max_num_suggestions=100)
|
25
|
+
url_params = {max_num_suggestions: max_num_suggestions}
|
26
|
+
execute_request(
|
27
|
+
api_path: "/asins/#{asin_value}/suggested/keywords",
|
28
|
+
request_type: :get,
|
29
|
+
url_params: url_params
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def asin_list(asin_list, max_num_suggestions=100)
|
34
|
+
url_params = {max_num_suggestions: max_num_suggestions}
|
35
|
+
execute_request(
|
36
|
+
api_path: '/asins/suggested/keywords',
|
37
|
+
request_type: :post,
|
38
|
+
payload: {
|
39
|
+
asins: asin_list,
|
40
|
+
max_num_suggestions: max_num_suggestions
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def execute_request(api_path: "", request_type:, payload: nil, url_params: nil)
|
48
|
+
url = "#{@base_url}#{api_path}"
|
49
|
+
|
50
|
+
request = Request.new(
|
51
|
+
url: url,
|
52
|
+
url_params: url_params,
|
53
|
+
request_type: request_type,
|
54
|
+
payload: payload,
|
55
|
+
headers: @headers
|
56
|
+
)
|
57
|
+
|
58
|
+
request.make_request
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|