soapy_bing 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +1 -0
  4. data/README.md +18 -18
  5. data/Rakefile +1 -0
  6. data/lib/soapy_bing.rb +5 -1
  7. data/lib/soapy_bing/account.rb +1 -0
  8. data/lib/soapy_bing/accounts.rb +13 -17
  9. data/lib/soapy_bing/ads.rb +18 -41
  10. data/lib/soapy_bing/ads/campaign_performance_report.rb +49 -0
  11. data/lib/soapy_bing/ads/campaigns.rb +106 -0
  12. data/lib/soapy_bing/ads/parsers/bulk_csv_parser.rb +34 -0
  13. data/lib/soapy_bing/ads/parsers/report_csv_parser.rb +45 -0
  14. data/lib/soapy_bing/ads/report.rb +93 -0
  15. data/lib/soapy_bing/campaign_management.rb +21 -0
  16. data/lib/soapy_bing/country_codes.rb +17 -0
  17. data/lib/soapy_bing/country_codes.yml +727 -0
  18. data/lib/soapy_bing/customer.rb +1 -0
  19. data/lib/soapy_bing/helpers.rb +1 -1
  20. data/lib/soapy_bing/helpers/zip_downloader.rb +1 -0
  21. data/lib/soapy_bing/oauth_credentials.rb +1 -0
  22. data/lib/soapy_bing/param_guard.rb +1 -0
  23. data/lib/soapy_bing/service.rb +69 -0
  24. data/lib/soapy_bing/service_operation.rb +62 -0
  25. data/lib/soapy_bing/version.rb +2 -1
  26. data/lib/soapy_bing/wsdl/ad_insight.wsdl +1 -0
  27. data/lib/soapy_bing/wsdl/bulk.wsdl +1 -0
  28. data/lib/soapy_bing/wsdl/campaign_management.wsdl +1 -0
  29. data/lib/soapy_bing/wsdl/customer_billing.wsdl +1 -0
  30. data/lib/soapy_bing/wsdl/customer_management.wsdl +1 -0
  31. data/lib/soapy_bing/wsdl/reporting.wsdl +1 -0
  32. data/lib/tasks/console.rake +1 -0
  33. data/lib/tasks/coverage.rake +1 -0
  34. data/lib/tasks/spec.rake +1 -0
  35. data/lib/tasks/upate_wsdl_files.rake +23 -0
  36. data/lib/tasks/update_country_codes.rake +24 -0
  37. data/soapy_bing.gemspec +2 -1
  38. data/spec/fixtures/ads/campaign_performance_report.csv +37 -0
  39. data/spec/fixtures/ads/campaign_performance_report.json +170 -0
  40. data/spec/fixtures/ads/campaigns_by_account_id.csv +7 -0
  41. data/spec/fixtures/{bulk/campaigns.json → ads/campaigns_by_account_id.json} +496 -240
  42. data/spec/fixtures/ads/campaigns_by_campaign_ids.csv +5 -0
  43. data/spec/fixtures/ads/campaigns_by_campaign_ids.json +910 -0
  44. data/spec/fixtures/vcr_cassettes/SoapyBing_Accounts/_list/returns_a_list_of_SoapyBing_Account_objects.yml +27 -27
  45. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads/_campaigns/by_account_id/returns_parsed_rows.yml +301 -0
  46. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads/_campaigns/by_campaign_ids/returns_parsed_rows.yml +300 -0
  47. data/spec/fixtures/vcr_cassettes/SoapyBing_CampaignManagement/_get_geo_locations/returns_a_list_of_geo_locations_hashes.yml +154 -0
  48. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_pending_status.yml +65 -94
  49. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_successful_empty_response.yml +511 -0
  50. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_successful_response.yml +384 -0
  51. data/spec/integration/soapy_bing/accounts_spec.rb +1 -0
  52. data/spec/integration/soapy_bing/ads_campaigns_spec.rb +85 -0
  53. data/spec/integration/soapy_bing/campaign_management_spec.rb +22 -0
  54. data/spec/integration/soapy_bing/oauth_credentials_spec.rb +1 -0
  55. data/spec/simplecov_setup.rb +1 -0
  56. data/spec/soapy_bing/account_spec.rb +6 -2
  57. data/spec/soapy_bing/accounts_spec.rb +47 -0
  58. data/spec/soapy_bing/ads/campaign_performance_report_spec.rb +79 -0
  59. data/spec/soapy_bing/ads/campaigns_spec.rb +99 -0
  60. data/spec/soapy_bing/ads/{bulk/parsers/csv_parser_spec.rb → parsers/bulk_csv_parser_spec.rb} +5 -4
  61. data/spec/soapy_bing/ads/{reports/parsers/csv_parser_spec.rb → parsers/report_csv_parser_spec.rb} +6 -4
  62. data/spec/soapy_bing/country_codes_spec.rb +39 -0
  63. data/spec/soapy_bing/helpers/zip_downloader_spec.rb +2 -0
  64. data/spec/soapy_bing/oauth_credentials_spec.rb +6 -2
  65. data/spec/soapy_bing/param_guard_spec.rb +2 -0
  66. data/spec/soapy_bing/service_spec.rb +56 -0
  67. data/spec/spec_helper.rb +1 -0
  68. data/spec/support/dotenv.rb +1 -0
  69. data/spec/support/vcr.rb +75 -6
  70. metadata +74 -118
  71. data/lib/soapy_bing/ads/bulk.rb +0 -3
  72. data/lib/soapy_bing/ads/bulk/campaigns.rb +0 -65
  73. data/lib/soapy_bing/ads/bulk/parsers.rb +0 -2
  74. data/lib/soapy_bing/ads/bulk/parsers/csv_parser.rb +0 -35
  75. data/lib/soapy_bing/ads/reports.rb +0 -4
  76. data/lib/soapy_bing/ads/reports/base.rb +0 -74
  77. data/lib/soapy_bing/ads/reports/campaign_performance_report.rb +0 -24
  78. data/lib/soapy_bing/ads/reports/parsers.rb +0 -2
  79. data/lib/soapy_bing/ads/reports/parsers/csv_parser.rb +0 -46
  80. data/lib/soapy_bing/helpers/class_name.rb +0 -10
  81. data/lib/soapy_bing/soap.rb +0 -4
  82. data/lib/soapy_bing/soap/request.rb +0 -10
  83. data/lib/soapy_bing/soap/request/base.rb +0 -38
  84. data/lib/soapy_bing/soap/request/download_campaigns_by_account_ids_request.rb +0 -17
  85. data/lib/soapy_bing/soap/request/get_accounts_info_request.rb +0 -17
  86. data/lib/soapy_bing/soap/request/get_ad_groups_by_campaign_id_request.rb +0 -17
  87. data/lib/soapy_bing/soap/request/get_ads_by_ad_group_id_request.rb +0 -17
  88. data/lib/soapy_bing/soap/request/get_bulk_download_status_request.rb +0 -17
  89. data/lib/soapy_bing/soap/request/get_targets_by_campaign_ids_request.rb +0 -17
  90. data/lib/soapy_bing/soap/request/poll_generate_report_request.rb +0 -34
  91. data/lib/soapy_bing/soap/request/submit_generate_report_request.rb +0 -17
  92. data/lib/soapy_bing/soap/response.rb +0 -14
  93. data/lib/soapy_bing/soap/response/base.rb +0 -17
  94. data/lib/soapy_bing/soap/response/download_campaigns_by_account_ids_response.rb +0 -12
  95. data/lib/soapy_bing/soap/response/get_accounts_info_response.rb +0 -12
  96. data/lib/soapy_bing/soap/response/get_ad_groups_by_campaign_id_response.rb +0 -12
  97. data/lib/soapy_bing/soap/response/get_ads_by_ad_group_id_response.rb +0 -12
  98. data/lib/soapy_bing/soap/response/get_bulk_download_status_response.rb +0 -17
  99. data/lib/soapy_bing/soap/response/get_targets_by_campaign_ids_response.rb +0 -12
  100. data/lib/soapy_bing/soap/response/payload.rb +0 -28
  101. data/lib/soapy_bing/soap/response/poll_generate_report_response.rb +0 -14
  102. data/lib/soapy_bing/soap/response/report_status.rb +0 -34
  103. data/lib/soapy_bing/soap/response/submit_generate_report_response.rb +0 -12
  104. data/lib/soapy_bing/soap/template_renderer.rb +0 -22
  105. data/lib/soapy_bing/soap/templates/download_campaigns_by_account_ids.xml.erb +0 -22
  106. data/lib/soapy_bing/soap/templates/get_accounts_info.xml.erb +0 -12
  107. data/lib/soapy_bing/soap/templates/get_ad_groups_by_campaign_id.xml.erb +0 -15
  108. data/lib/soapy_bing/soap/templates/get_ads_by_ad_group_id.xml.erb +0 -15
  109. data/lib/soapy_bing/soap/templates/get_bulk_download_status.xml.erb +0 -15
  110. data/lib/soapy_bing/soap/templates/get_targets_by_campaign_ids.xml.erb +0 -20
  111. data/lib/soapy_bing/soap/templates/poll_generate_report.xml.erb +0 -18
  112. data/lib/soapy_bing/soap/templates/submit_generate_report.xml.erb +0 -46
  113. data/spec/fixtures/bulk/campaigns.csv +0 -8
  114. data/spec/fixtures/get_ad_groups_by_campaign_id.json +0 -587
  115. data/spec/fixtures/get_ads_by_ad_group_id.json +0 -218
  116. data/spec/fixtures/get_targets_by_campaign_ids.json +0 -81
  117. data/spec/fixtures/reports/campaign_performance_report.csv +0 -37
  118. data/spec/fixtures/reports/campaign_performance_report.json +0 -146
  119. data/spec/fixtures/soap_templates/simple.xml.erb +0 -2
  120. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads/_get_ad_groups_by_campaign_id/1_1_1.yml +0 -150
  121. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads/_get_ads_by_ad_group_id/1_2_1.yml +0 -141
  122. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads/_get_targets_by_campaign_ids/1_3_1.yml +0 -111
  123. data/spec/fixtures/vcr_cassettes/SoapyBing_Ads_Bulk_Campaigns/_result_file_url/returns_result_file_url.yml +0 -216
  124. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_successful_status.yml +0 -284
  125. data/spec/integration/soapy_bing/ads/bulk/campaigns_spec.rb +0 -35
  126. data/spec/integration/soapy_bing/ads/reports/campaign_performance_report_spec.rb +0 -40
  127. data/spec/integration/soapy_bing/ads_spec.rb +0 -32
  128. data/spec/soapy_bing/ads/reports/campaign_performance_report_spec.rb +0 -42
  129. data/spec/soapy_bing/ads_spec.rb +0 -33
  130. data/spec/soapy_bing/helpers/class_name_spec.rb +0 -15
  131. data/spec/soapy_bing/soap/request/base_spec.rb +0 -55
  132. data/spec/soapy_bing/soap/request/poll_generate_report_request_spec.rb +0 -60
  133. data/spec/soapy_bing/soap/response/base_spec.rb +0 -13
  134. data/spec/soapy_bing/soap/response/get_bulk_download_status_response_spec.rb +0 -60
  135. data/spec/soapy_bing/soap/response/payload_spec.rb +0 -48
  136. data/spec/soapy_bing/soap/response/poll_generate_report_response_spec.rb +0 -26
  137. data/spec/soapy_bing/soap/response/report_status_spec.rb +0 -92
  138. data/spec/soapy_bing/soap/response/submit_generate_report_response_spec.rb +0 -20
  139. data/spec/soapy_bing/soap/template_renderer_spec.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6769b40c74197462ce64b3858e1d9685a96a2afc
4
- data.tar.gz: e7cd2d208675f7318e838318e9ba3f7598a95d2c
3
+ metadata.gz: 6516951a384d76b68b51cbd390f1d66fb7b41f8d
4
+ data.tar.gz: aa5436b3258ebfda01f6b832782242f0d6c39ee4
5
5
  SHA512:
6
- metadata.gz: 9d9c735dccb584cc54a9de1a4827a481393f24a4ab7e086c0e3ac9770d9c0d272fbedda8d8da233e2fcb8a78132663be31a3c58be26c36e1ee5525dfb1e21990
7
- data.tar.gz: 9c19239300bc2ee50a79a3c87cdb995d24a7e37b7e029c976cc735ce7683088faed872e76f383ba9397d76b46df3175a9fa36ef40ed696cd1d6347a82a9b6aec
6
+ metadata.gz: 4472f06bcc20a6520359a9542d4361e61969214373c40bfbf909d93751de2b7e584d082aa31cadcb94f7dea6fade3d17f03f31d96849117971dd627cd69981b1
7
+ data.tar.gz: 2c7a31b9d70b3b3c6d82c1532566e456d546b0ef48875773eda984ce80002c1c62bfb8dadea6c54a02176edc0fd99829e04beca9f903461f1ee92287b2e832d3
@@ -1 +1 @@
1
- 2.3.1
1
+ 2.3.4
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  source 'https://rubygems.org'
3
4
 
4
5
  gemspec
data/README.md CHANGED
@@ -8,41 +8,41 @@ Client library for [Bing Ads API](https://msdn.microsoft.com/en-us/library/bing-
8
8
  In your Gemfile
9
9
 
10
10
  ```ruby
11
- gem 'soapy_bing', git: 'https://github.com/ad2games/soapy_bing'
11
+ gem 'soapy_bing'
12
12
  ```
13
13
 
14
14
  ## Usage Examples
15
15
 
16
16
  ```ruby
17
17
  bing_ads = SoapyBing::Ads.new
18
- report = bing_ads.campaign_performance_report(
19
- date_start: '2015-10-14',
20
- date_end: '2015-10-14',
21
-
18
+ rows = bing_ads.campaign_performance_report(
19
+ date_start: '2017-05-14',
20
+ date_end: '2017-05-14',
22
21
  settings: { # optional
23
- columns: [''] # see SoapyBing::Ads::Report::Base::DEFAULT_REPORT_SETTINGS[:columns]
22
+ columns: [''] # see SoapyBing::Report::Base::DEFAULT_REPORT_SETTINGS[:columns]
24
23
  }
25
24
  )
26
- report.rows # =>
27
- # [{
28
- # "HourOfDay" => "0",
29
- # "CampaignName" => "My Campaign",
30
- # "Impressions" => "100",
31
- # "Clicks" => "47",
32
- # "Spend" => "12.65"
33
- # }, {
34
- # ...
35
- # }]
25
+ # =>
26
+ # [{
27
+ # "GregorianDate" => "2017-05-14"
28
+ # "Hour" => "0",
29
+ # "CampaignName" => "My Campaign",
30
+ # "Impressions" => "100",
31
+ # "Clicks" => "47",
32
+ # "Spend" => "12.65"
33
+ # }, {
34
+ # ...
35
+ # }]
36
36
  ```
37
37
 
38
38
  ## Authentication
39
39
 
40
40
  Authentication attributes could be passed explicitly when new instance of SoapyBing::Ads is created.
41
- Or they could be configured as Envronment variables.
41
+ Or they could be configured as Environment variables.
42
42
 
43
43
  ```ruby
44
44
  SoapyBing::Ads.new(
45
- oauth: { # optional, could be configured via environment variables as
45
+ oauth_credentials: { # optional, could be configured via environment variables as
46
46
  client_id: '', # ENV['BING_ADS_OAUTH_CLIENT_ID']
47
47
  client_secret: '', # ENV['BING_ADS_OAUTH_CLIENT_SECRET']
48
48
  refresh_token: '' # ENV['BING_ADS_OAUTH_REFRESH_TOKEN']
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'rubygems'
3
4
  require 'bundler/setup'
4
5
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'httparty'
3
4
  require 'retryable'
4
5
  require 'active_support'
@@ -9,6 +10,9 @@ require 'soapy_bing/param_guard'
9
10
  require 'soapy_bing/customer'
10
11
  require 'soapy_bing/account'
11
12
  require 'soapy_bing/oauth_credentials'
12
- require 'soapy_bing/soap'
13
13
  require 'soapy_bing/ads'
14
14
  require 'soapy_bing/accounts'
15
+ require 'soapy_bing/service'
16
+ require 'soapy_bing/service_operation'
17
+ require 'soapy_bing/campaign_management'
18
+ require 'soapy_bing/country_codes'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module SoapyBing
3
4
  class Account
4
5
  attr_reader :developer_token, :account_id, :customer_id
@@ -1,32 +1,28 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module SoapyBing
3
4
  class Accounts
4
- attr_reader :oauth_credentials, :customer
5
+ attr_reader :service
5
6
 
6
- def initialize(oauth: {}, customer: {})
7
- @oauth_credentials = OauthCredentials.new(oauth)
8
- @customer = Customer.new(customer)
7
+ def initialize(*args)
8
+ @service = Service.customer_management(*args)
9
9
  end
10
10
 
11
11
  def list
12
- do_request(Soap::Request::GetAccountsInfoRequest).map do |account|
13
- Account.new(
14
- developer_token: customer.developer_token,
15
- customer_id: customer.customer_id,
16
- account_id: account['Id']
17
- )
12
+ response = service.get_accounts_info
13
+ response[:accounts_info][:account_info].map do |account_info|
14
+ build_account(account_info)
18
15
  end
19
16
  end
20
17
 
21
18
  private
22
19
 
23
- def do_request(klass, options = {})
24
- klass.new(context: {
25
- authentication_token: oauth_credentials.access_token,
26
- developer_token: customer.developer_token
27
- }.merge(options))
28
- .perform
29
- .payload
20
+ def build_account(account_info)
21
+ Account.new(
22
+ developer_token: service.customer.developer_token,
23
+ customer_id: service.customer.customer_id,
24
+ account_id: account_info[:id]
25
+ )
30
26
  end
31
27
  end
32
28
  end
@@ -1,56 +1,33 @@
1
1
  # frozen_string_literal: true
2
- require 'soapy_bing/ads/reports'
3
- require 'soapy_bing/ads/bulk'
2
+
3
+ require 'soapy_bing/ads/campaigns'
4
+ require 'soapy_bing/ads/campaign_performance_report'
4
5
 
5
6
  module SoapyBing
6
7
  class Ads
7
- attr_reader :oauth_credentials, :account
8
+ attr_reader :service_options
8
9
 
9
- def initialize(oauth: {}, account: {})
10
- @oauth_credentials = OauthCredentials.new(oauth)
11
- @account = Account.new(account)
10
+ def initialize(service_options = {})
11
+ @service_options = service_options
12
12
  end
13
13
 
14
- def campaign_performance_report(date_start:, date_end:, settings: {})
15
- Reports::CampaignPerformanceReport.new(
16
- oauth_credentials: oauth_credentials,
17
- account: account,
14
+ def campaign_performance_report(date_start:, date_end:, settings: {}, polling_settings: {})
15
+ CampaignPerformanceReport.new(
16
+ service_options: service_options,
18
17
  date_start: date_start,
19
18
  date_end: date_end,
20
- settings: settings
21
- )
22
- end
23
-
24
- def bulk_campaigns(entities = nil, polling_settings = {})
25
- Bulk::Campaigns.new(
26
- oauth_credentials: oauth_credentials,
27
- account: account,
28
- entities: entities,
19
+ settings: settings,
29
20
  polling_settings: polling_settings
30
- )
31
- end
32
-
33
- def get_ad_groups_by_campaign_id(campaign_id)
34
- do_request(Soap::Request::GetAdGroupsByCampaignIdRequest, campaign_id: campaign_id)
21
+ ).rows
35
22
  end
36
23
 
37
- def get_ads_by_ad_group_id(ad_group_id)
38
- do_request(Soap::Request::GetAdsByAdGroupIdRequest, ad_group_id: ad_group_id)
39
- end
40
-
41
- def get_targets_by_campaign_ids(campaign_ids)
42
- do_request(Soap::Request::GetTargetsByCampaignIdsRequest, campaign_ids: campaign_ids)
43
- end
44
-
45
- private
46
-
47
- def do_request(klass, options)
48
- klass.new(context: {
49
- account: account,
50
- authentication_token: oauth_credentials.access_token
51
- }.merge(options))
52
- .perform
53
- .payload
24
+ def campaigns(entities = Campaigns::DEFAULT_ENTITIES, polling_settings: {}, campaign_ids: nil)
25
+ Campaigns.new(
26
+ service_options: service_options,
27
+ entities: entities,
28
+ polling_settings: polling_settings,
29
+ campaign_ids: campaign_ids
30
+ ).rows
54
31
  end
55
32
  end
56
33
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'soapy_bing/ads/report'
4
+ require 'date'
5
+
6
+ module SoapyBing
7
+ class Ads
8
+ class CampaignPerformanceReport < Report
9
+ attr_reader :date_start, :date_end
10
+
11
+ def initialize(options)
12
+ super
13
+ @date_start = Date.parse(options.fetch(:date_start))
14
+ @date_end = Date.parse(options.fetch(:date_end))
15
+ end
16
+
17
+ def message
18
+ {
19
+ aggregation: settings.aggregation,
20
+ columns: { campaign_performance_report_column: settings.columns },
21
+ scope: scope,
22
+ time: {
23
+ custom_date_range_end: date_hash(date_end),
24
+ custom_date_range_start: date_hash(date_start)
25
+ }
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def scope
32
+ {
33
+ account_ids: {
34
+ '@xmlns:a1' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays',
35
+ 'a1:long' => service.account.account_id
36
+ }
37
+ }
38
+ end
39
+
40
+ def date_hash(date)
41
+ { day: date.day, month: date.month, year: date.year }
42
+ end
43
+
44
+ def request_type
45
+ 'CampaignPerformanceReportRequest'
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'soapy_bing/ads/parsers/bulk_csv_parser'
4
+
5
+ module SoapyBing
6
+ class Ads
7
+ class Campaigns
8
+ DEFAULT_ENTITIES = %w[CampaignTargetCriterions Ads].freeze
9
+ DEFAULT_POLLING_SETTINGS = {
10
+ tries: 20,
11
+ sleep: ->(n) { n < 7 ? 2**n : 120 }
12
+ }.freeze
13
+ NotCompleted = Class.new(StandardError)
14
+ StatusFailed = Class.new(StandardError)
15
+
16
+ attr_reader :service, :entities, :polling_settings, :status, :campaign_ids
17
+
18
+ def initialize(
19
+ service_options:,
20
+ entities: DEFAULT_ENTITIES,
21
+ polling_settings: {},
22
+ campaign_ids: nil
23
+ )
24
+ @service = Service.bulk(service_options)
25
+ @entities = entities
26
+ @polling_settings = DEFAULT_POLLING_SETTINGS.merge(polling_settings)
27
+ @campaign_ids = campaign_ids
28
+ end
29
+
30
+ def rows
31
+ @rows ||= Parsers::BulkCsvParser.new(Helpers::ZipDownloader.new(result_file_url).read).rows
32
+ end
33
+
34
+ private
35
+
36
+ def result_file_url
37
+ @result_file_url ||= begin
38
+ wait_status_complete
39
+ status[:result_file_url]
40
+ end
41
+ end
42
+
43
+ def fetch_status
44
+ response = service.get_bulk_download_status(request_id: download_request_id)
45
+ if response[:request_status] == 'Failed'
46
+ raise StatusFailed, response[:errors].to_s
47
+ end
48
+ @status = response.slice(:percent_complete, :request_status, :result_file_url)
49
+ end
50
+
51
+ def wait_status_complete
52
+ Retryable.retryable(polling_settings.merge(on: NotCompleted)) do
53
+ fetch_status
54
+ raise NotCompleted if status[:request_status] != 'Completed'
55
+ end
56
+ end
57
+
58
+ def download_request_id
59
+ @download_request_id ||= download_campaigns
60
+ end
61
+
62
+ def download_campaigns
63
+ response = campaign_ids ? by_campaign_ids : by_account_ids
64
+ response[:download_request_id]
65
+ end
66
+
67
+ def by_campaign_ids
68
+ service.download_campaigns_by_campaign_ids(
69
+ {
70
+ campaigns: {
71
+ campaign_scope: campaign_scope
72
+ }
73
+ }.merge(common_request_body)
74
+ )
75
+ end
76
+
77
+ def campaign_scope
78
+ campaign_ids.map do |campaign_id|
79
+ {
80
+ campaign_id: campaign_id,
81
+ parent_account_id: service.account.account_id
82
+ }
83
+ end
84
+ end
85
+
86
+ def by_account_ids
87
+ service.download_campaigns_by_account_ids(
88
+ {
89
+ account_ids: {
90
+ '@xmlns:a1' => 'http://schemas.microsoft.com/2003/10/Serialization/Arrays',
91
+ 'a1:long' => service.account.account_id
92
+ }
93
+ }.merge(common_request_body)
94
+ )
95
+ end
96
+
97
+ def common_request_body
98
+ {
99
+ download_entities: { download_entity: entities },
100
+ download_file_type: 'Csv',
101
+ format_version: '5.0'
102
+ }
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+
5
+ module SoapyBing
6
+ class Ads
7
+ module Parsers
8
+ class BulkCsvParser
9
+ def initialize(raw)
10
+ @raw = raw
11
+ end
12
+
13
+ def rows
14
+ @rows ||= begin
15
+ header, *body = extract_csv_payload
16
+ body.map { |row| header.zip(row).to_h }
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :raw
23
+
24
+ def extract_csv_payload
25
+ text = raw.dup
26
+ text.force_encoding(Encoding::UTF_8).encode! unless text.encoding == Encoding::UTF_8
27
+ text.sub!(/^\xEF\xBB\xBF/, '') # cleanup BOM
28
+
29
+ CSV.parse(text)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+
5
+ module SoapyBing
6
+ class Ads
7
+ module Parsers
8
+ class ReportCsvParser
9
+ class FormatError < StandardError; end
10
+
11
+ CSV_PAYLOAD_OFFSET_FRONT = 10 # First 10 csv lines are report metadata
12
+ CSV_PAYLOAD_OFFSET_BACK = 2 # Last 2 csv lines are Microsoft copyright
13
+
14
+ def initialize(raw)
15
+ @raw = raw
16
+ end
17
+
18
+ def rows
19
+ @rows ||= begin
20
+ header, *body = extract_csv_payload
21
+ raise FormatError if body.size != payload_rows_number
22
+ body.map { |row| header.zip(row).to_h }
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :raw
29
+
30
+ def extract_csv_payload
31
+ text = raw.dup
32
+ text.force_encoding(Encoding::UTF_8).encode! unless text.encoding == Encoding::UTF_8
33
+ text.sub!(/^\xEF\xBB\xBF/, '') # cleanup BOM
34
+
35
+ csv_rows = CSV.parse(text)
36
+ csv_rows[CSV_PAYLOAD_OFFSET_FRONT...-CSV_PAYLOAD_OFFSET_BACK]
37
+ end
38
+
39
+ def payload_rows_number
40
+ raw.match(/"Rows: (\d+)"/)[1].to_i
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end