blurb 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,48 +1,52 @@
1
- module Blurb
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
- def create(params = {}, opts = {})
11
- # required argument checks
12
- raise ArgumentError.new("params hash must contain a recordType") unless params["recordType"]
13
-
14
- # If no metrics are passed in, use the default metrics
15
- metrics = params["metrics"] || get_default_metrics(params["recordType"],params["campaignType"])
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
- api_params = {
18
- "reportDate" => params["reportDate"],
19
- "metrics" => metrics
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
- api_params["segment"] = params["segment"] if params["segment"]
23
-
24
- if params["recordType"] == ASINS
25
- request_url = "/v2/#{ASINS}/report"
26
- api_params["campaignType"] = 'sponsoredProducts' if params["campaignType"] == SPONSORED_PRODUCTS
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 status(report_id, opts = {})
36
- get_request("/v2/reports/#{report_id}")
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(location, opts = {})
40
- opts.merge!({:full_path => true, :gzip => true, :no_token => true})
41
- get_request(location, opts)
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
- def get_default_metrics(record_type, campaign_type)
45
- if campaign_type == SPONSORED_BRANDS
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
- ].join(",") if record_type == CAMPAIGNS
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
- ].join(",") if record_type == AD_GROUPS
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
- ].join(",") if record_type == KEYWORDS
79
- elsif campaign_type == SPONSORED_PRODUCTS
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
- ].join(",") if record_type == CAMPAIGNS
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
- ].join(",") if record_type == AD_GROUPS
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
- ].join(",") if record_type == KEYWORDS
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
- ].join(",") if record_type == PRODUCT_ADS
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
- ].join(",") if record_type == TARGETS
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
- ].join(",") if record_type == PORTFOLIOS
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