soapy_bing 0.0.1

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.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.env +6 -0
  3. data/.gitignore +6 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +6 -0
  7. data/README.md +63 -0
  8. data/Rakefile +7 -0
  9. data/circle.yml +6 -0
  10. data/lib/soapy_bing/account.rb +12 -0
  11. data/lib/soapy_bing/ads/reports/base.rb +83 -0
  12. data/lib/soapy_bing/ads/reports/campaign_performance_report.rb +23 -0
  13. data/lib/soapy_bing/ads/reports/parsers/csv_parser.rb +45 -0
  14. data/lib/soapy_bing/ads/reports/parsers.rb +1 -0
  15. data/lib/soapy_bing/ads/reports.rb +3 -0
  16. data/lib/soapy_bing/ads.rb +22 -0
  17. data/lib/soapy_bing/helpers/class_name.rb +9 -0
  18. data/lib/soapy_bing/helpers.rb +1 -0
  19. data/lib/soapy_bing/oauth_credentials.rb +43 -0
  20. data/lib/soapy_bing/param_guard.rb +26 -0
  21. data/lib/soapy_bing/soap/request/base.rb +37 -0
  22. data/lib/soapy_bing/soap/request/poll_generate_report_request.rb +33 -0
  23. data/lib/soapy_bing/soap/request/submit_generate_report_request.rb +16 -0
  24. data/lib/soapy_bing/soap/request.rb +3 -0
  25. data/lib/soapy_bing/soap/response/base.rb +16 -0
  26. data/lib/soapy_bing/soap/response/payload.rb +15 -0
  27. data/lib/soapy_bing/soap/response/poll_generate_report_response.rb +13 -0
  28. data/lib/soapy_bing/soap/response/report_status.rb +33 -0
  29. data/lib/soapy_bing/soap/response/submit_generate_report_response.rb +11 -0
  30. data/lib/soapy_bing/soap/response.rb +5 -0
  31. data/lib/soapy_bing/soap/template_renderer.rb +21 -0
  32. data/lib/soapy_bing/soap/templates/poll_generate_report.erb.xml +18 -0
  33. data/lib/soapy_bing/soap/templates/submit_generate_report.erb.xml +46 -0
  34. data/lib/soapy_bing/soap.rb +3 -0
  35. data/lib/soapy_bing/version.rb +3 -0
  36. data/lib/soapy_bing.rb +11 -0
  37. data/lib/tasks/console.rake +5 -0
  38. data/lib/tasks/coverage.rake +6 -0
  39. data/lib/tasks/spec.rake +4 -0
  40. data/soapy_bing.gemspec +34 -0
  41. data/spec/fixtures/reports/campaign_performance_report.csv +37 -0
  42. data/spec/fixtures/reports/campaign_performance_report.json +146 -0
  43. data/spec/fixtures/soap_templates/simple.erb.xml +2 -0
  44. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_pending_status.yml +168 -0
  45. data/spec/fixtures/vcr_cassettes/campaign_performance_report/with_successful_status.yml +284 -0
  46. data/spec/fixtures/vcr_cassettes/oauth_credentials/access_token/with_successful_status.yml +42 -0
  47. data/spec/integration/soapy_bing/ads/reports/campaign_performance_report_spec.rb +39 -0
  48. data/spec/integration/soapy_bing/oauth_credentials_spec.rb +10 -0
  49. data/spec/simplecov_setup.rb +9 -0
  50. data/spec/soapy_bing/account_spec.rb +80 -0
  51. data/spec/soapy_bing/ads/reports/campaign_performance_report_spec.rb +41 -0
  52. data/spec/soapy_bing/ads/reports/parsers/csv_parser_spec.rb +31 -0
  53. data/spec/soapy_bing/ads_spec.rb +32 -0
  54. data/spec/soapy_bing/helpers/class_name_spec.rb +14 -0
  55. data/spec/soapy_bing/oauth_credentials_spec.rb +108 -0
  56. data/spec/soapy_bing/param_guard_spec.rb +43 -0
  57. data/spec/soapy_bing/soap/request/base_spec.rb +54 -0
  58. data/spec/soapy_bing/soap/request/poll_generate_report_request_spec.rb +59 -0
  59. data/spec/soapy_bing/soap/response/base_spec.rb +12 -0
  60. data/spec/soapy_bing/soap/response/payload_spec.rb +25 -0
  61. data/spec/soapy_bing/soap/response/poll_generate_report_response_spec.rb +25 -0
  62. data/spec/soapy_bing/soap/response/report_status_spec.rb +91 -0
  63. data/spec/soapy_bing/soap/response/submit_generate_report_response_spec.rb +19 -0
  64. data/spec/soapy_bing/soap/template_renderer_spec.rb +24 -0
  65. data/spec/spec_helper.rb +32 -0
  66. data/spec/support/dotenv.rb +3 -0
  67. data/spec/support/vcr.rb +101 -0
  68. metadata +305 -0
@@ -0,0 +1,108 @@
1
+ RSpec.describe SoapyBing::OauthCredentials do
2
+ describe '#initialize' do
3
+ subject { described_class.new(credentials) }
4
+
5
+ context 'when oauth credentials passed explicitly' do
6
+ let(:credentials) { { client_id: 'foo', client_secret: 'bar', refresh_token: 'baz' } }
7
+ before do
8
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_CLIENT_ID').and_return('foo_env')
9
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_CLIENT_SECRET').and_return('bar_env')
10
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_REFRESH_TOKEN').and_return('baz_env')
11
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_TOKEN_URL')
12
+ end
13
+
14
+ it '#client_id is set' do
15
+ expect(subject.client_id).to eq 'foo'
16
+ end
17
+
18
+ it '#client_secret is set' do
19
+ expect(subject.client_secret).to eq 'bar'
20
+ end
21
+
22
+ it '#refresh_token is set' do
23
+ expect(subject.refresh_token).to eq 'baz'
24
+ end
25
+ end
26
+
27
+ context 'when oauth credentials passed via Enviromenment variables' do
28
+ let(:credentials) { {} }
29
+ before do
30
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_CLIENT_ID').and_return('foo_env')
31
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_CLIENT_SECRET').and_return('bar_env')
32
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_REFRESH_TOKEN').and_return('baz_env')
33
+ allow(ENV).to receive(:[]).with('BING_ADS_OAUTH_TOKEN_URL')
34
+ end
35
+
36
+ it '#client_id is set' do
37
+ expect(subject.client_id).to eq 'foo_env'
38
+ end
39
+
40
+ it '#client_secret is set' do
41
+ expect(subject.client_secret).to eq 'bar_env'
42
+ end
43
+
44
+ it '#refresh_token is set' do
45
+ expect(subject.refresh_token).to eq 'baz_env'
46
+ end
47
+ end
48
+
49
+ context 'when no oauth credentials passed' do
50
+ let(:credentials) { { client_id: 'foo', client_secret: 'bar', refresh_token: 'baz' } }
51
+ before do
52
+ %w( BING_ADS_OAUTH_CLIENT_ID
53
+ BING_ADS_OAUTH_CLIENT_SECRET
54
+ BING_ADS_OAUTH_REFRESH_TOKEN ).each do |var|
55
+ allow(ENV).to receive(:[]).with(var).and_return(nil)
56
+ end
57
+ end
58
+
59
+ it 'throws exception on missing :client_id' do
60
+ credentials.delete(:client_id)
61
+ expect { subject }.to raise_error SoapyBing::ParamGuard::ParamRequiredError,
62
+ 'client_id have to be passed explicitly or via ENV[\'BING_ADS_OAUTH_CLIENT_ID\']'
63
+ end
64
+
65
+ it 'throws exception on missing :client_secret' do
66
+ credentials.delete(:client_secret)
67
+ expect { subject }.to raise_error SoapyBing::ParamGuard::ParamRequiredError,
68
+ 'client_secret have to be passed explicitly or via ENV[\'BING_ADS_OAUTH_CLIENT_SECRET\']'
69
+ end
70
+
71
+ it 'throws exception on missing :refresh_token' do
72
+ credentials.delete(:refresh_token)
73
+ expect { subject }.to raise_error SoapyBing::ParamGuard::ParamRequiredError,
74
+ 'refresh_token have to be passed explicitly or via ENV[\'BING_ADS_OAUTH_REFRESH_TOKEN\']'
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#access_token' do
80
+ let(:credentials) { { client_id: 'foo', client_secret: 'bar', refresh_token: 'baz' } }
81
+ let(:response) { double(:response) }
82
+
83
+ before do
84
+ expect(response).to receive(:code).once.and_return(status_code)
85
+ expect(HTTParty).to receive(:post).once.and_return(response)
86
+ end
87
+
88
+ context 'when there is good response' do
89
+ let(:status_code) { 200 }
90
+
91
+ before { expect(response).to receive(:[]).once.with('access_token').and_return('my-token') }
92
+
93
+ it 'memoizes http request response' do
94
+ 2.times { expect(subject.access_token).to eq 'my-token' }
95
+ end
96
+ end
97
+
98
+ context 'when there is bad response' do
99
+ let(:status_code) { 401 }
100
+
101
+ it 'throws exception in case of bad status code' do
102
+ expect { subject.access_token }.to raise_error(
103
+ SoapyBing::OauthCredentials::TokenRefreshError
104
+ )
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,43 @@
1
+ RSpec.describe SoapyBing::ParamGuard do
2
+ describe '#require!' do
3
+ let(:param_guard) { described_class.new(options, env_namespace: 'MY') }
4
+ subject { param_guard.require!(:foo) }
5
+
6
+ context 'when option is empty' do
7
+ let(:options) { {} }
8
+
9
+ context 'and environment variable is empty too' do
10
+ it 'thows exception' do
11
+ expect { subject }.to raise_exception SoapyBing::ParamGuard::ParamRequiredError,
12
+ 'foo have to be passed explicitly or via ENV[\'MY_FOO\']'
13
+ end
14
+ end
15
+
16
+ context 'but environment variable is present' do
17
+ before { allow(ENV).to receive(:[]).with('MY_FOO').and_return('bar_env') }
18
+
19
+ it 'returns environment variable value' do
20
+ expect(subject).to eq 'bar_env'
21
+ end
22
+ end
23
+ end
24
+
25
+ context 'when option is present' do
26
+ let(:options) { { foo: 'bar' } }
27
+
28
+ context 'and environment variable is present too' do
29
+ before { allow(ENV).to receive(:[]).with('MY_FOO').and_return('bar_env') }
30
+
31
+ it 'returns option value' do
32
+ expect(subject).to eq 'bar'
33
+ end
34
+ end
35
+
36
+ context 'but environment variable is empty' do
37
+ it 'returns option value' do
38
+ expect(subject).to eq 'bar'
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,54 @@
1
+ RSpec.describe SoapyBing::Soap::Request::Base do
2
+ let(:req_context) { { foo: 'Bar' } }
3
+ subject { described_class.new(context: req_context) }
4
+
5
+ before { stub_const('MyCustomRequest', Class.new(described_class) {}) }
6
+ let(:my_custom_request) { MyCustomRequest.new(context: req_context) }
7
+
8
+ describe '#context' do
9
+ it 'keeps initialized value' do
10
+ expect(subject.context).to eq req_context
11
+ end
12
+ end
13
+
14
+ describe '#post' do
15
+ let(:body) { 'my body' }
16
+ let(:headers) { { 'My' => 'Header' } }
17
+
18
+ it 'delegates post request to HTTParty' do
19
+ expect(HTTParty).to receive(:post).with(
20
+ 'http://example.com',
21
+ hash_including(body: body, headers: hash_including(headers))
22
+ )
23
+ subject.post('http://example.com', body: body, headers: headers)
24
+ end
25
+ end
26
+
27
+ describe '#default_body' do
28
+ subject { my_custom_request.default_body }
29
+ it 'renders request body template' do
30
+ renderer = double('Renderer')
31
+ expect(SoapyBing::Soap::TemplateRenderer).to receive(:new)
32
+ .with(hash_including(req_context)).and_return(renderer)
33
+ expect(renderer).to receive(:render).with('my_custom')
34
+ subject
35
+ end
36
+ end
37
+
38
+ describe '#default_headers' do
39
+ subject { my_custom_request.default_headers }
40
+ it 'contains soap action' do
41
+ expect(subject).to include('SOAPAction' => 'MyCustom')
42
+ end
43
+ it 'contains default headers' do
44
+ expect(subject).to include(described_class::DEFAULT_HTTP_HEADERS)
45
+ end
46
+ end
47
+
48
+ describe '#action_name' do
49
+ subject { my_custom_request.action_name }
50
+ it 'resolves request\'s class soap action' do
51
+ expect(subject).to eq 'MyCustom'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ RSpec.describe SoapyBing::Soap::Request::PollGenerateReportRequest do
2
+ describe '#perform' do
3
+ let(:response_body) do
4
+ {
5
+ 'Envelope' => {
6
+ 'Body' => {
7
+ 'PollGenerateReportResponse' => {
8
+ 'ReportRequestStatus' => {
9
+ 'Status' => nil,
10
+ 'ReportDownloadUrl' => 'http://example.com'
11
+ }
12
+ }
13
+ }
14
+ }
15
+ }
16
+ end
17
+
18
+ let(:pending_response_body) do
19
+ response_body['Envelope']['Body']['PollGenerateReportResponse']['ReportRequestStatus']
20
+ .merge!('Status' => 'Pending')
21
+ response_body
22
+ end
23
+ let(:successful_response_body) do
24
+ response_body['Envelope']['Body']['PollGenerateReportResponse']['ReportRequestStatus']
25
+ .merge!('Status' => 'Success')
26
+ response_body
27
+ end
28
+
29
+ before do
30
+ call_count = 0
31
+ allow(HTTParty).to receive(:post) do
32
+ call_count += 1
33
+ call_count == 3 ? successful_response_body : pending_response_body
34
+ end
35
+ end
36
+
37
+ subject do
38
+ described_class
39
+ .new(
40
+ context: {
41
+ oauth: double(:oauth_credentials).as_null_object,
42
+ account: double(:account).as_null_object
43
+ }
44
+ )
45
+ .perform
46
+ end
47
+
48
+ it 'polls until successful response' do
49
+ expect(HTTParty).to receive(:post).exactly(3).times
50
+ expect(subject.payload).to eq 'http://example.com'
51
+ end
52
+
53
+ it 'throws PollingTimeoutError when exceeded polling tries' do
54
+ stub_const('SoapyBing::Soap::Request::PollGenerateReportRequest::POLLING_TRIES', 1)
55
+ expect(HTTParty).to receive(:post).once
56
+ expect { subject }.to raise_error described_class::PollingTimeoutError
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,12 @@
1
+ RSpec.describe SoapyBing::Soap::Response::Base do
2
+ before { stub_const('MyCustomResponse', Class.new(described_class) {}) }
3
+ let(:response_body) { 'Some response body' }
4
+ let(:my_custom_response) { MyCustomResponse.new(response_body) }
5
+
6
+ describe '#body' do
7
+ subject { my_custom_response.body }
8
+ it 'keeps initialized value' do
9
+ expect(subject).to eq response_body
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe SoapyBing::Soap::Response::Payload do
2
+ before do
3
+ stub_const(
4
+ 'MyCustomResponse',
5
+ Class
6
+ .new
7
+ .include(described_class)
8
+ )
9
+ end
10
+
11
+ subject { MyCustomResponse.new }
12
+
13
+ describe '#payload' do
14
+ it 'memoize #extract_payload value' do
15
+ expect_any_instance_of(MyCustomResponse).to receive(:extract_payload).once.and_return(true)
16
+ 2.times { subject.payload }
17
+ end
18
+ end
19
+
20
+ describe '#extract_payload' do
21
+ it 'throws NotImplementedError' do
22
+ expect { subject.extract_payload }.to raise_error NotImplementedError
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe SoapyBing::Soap::Response::PollGenerateReportResponse do
2
+ let(:url) { 'http://my-site.com' }
3
+ let(:response_hash) do
4
+ {
5
+ 'Envelope' => {
6
+ 'Body' => {
7
+ 'PollGenerateReportResponse' => {
8
+ 'ReportRequestStatus' => {
9
+ 'ReportDownloadUrl' => url
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
15
+ end
16
+ let(:subject) { described_class.new(response_hash) }
17
+
18
+ it 'includes ReportStatus' do
19
+ expect(described_class.ancestors).to include SoapyBing::Soap::Response::ReportStatus
20
+ end
21
+
22
+ it '#extract_payload returns download url' do
23
+ expect(subject.extract_payload).to eq url
24
+ end
25
+ end
@@ -0,0 +1,91 @@
1
+ RSpec.describe SoapyBing::Soap::Response::ReportStatus do
2
+ before do
3
+ stub_const(
4
+ 'MyCustomResponse',
5
+ Class
6
+ .new(SoapyBing::Soap::Response::Base)
7
+ .include(described_class)
8
+ )
9
+ end
10
+ let(:response_hash) do
11
+ {
12
+ 'Envelope' => {
13
+ 'Body' => {
14
+ 'MyCustomResponse' => {
15
+ 'ReportRequestStatus' => {
16
+ 'Status' => nil
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
22
+ end
23
+ let(:subject) { MyCustomResponse.new(response_hash) }
24
+
25
+ describe 'status' do
26
+ before do
27
+ response_hash['Envelope']['Body']['MyCustomResponse']['ReportRequestStatus']
28
+ .merge!('Status' => status)
29
+ end
30
+
31
+ context 'when error' do
32
+ let(:status) { 'Error' }
33
+
34
+ it '#status is Error' do
35
+ expect(subject.status).to eq status
36
+ end
37
+
38
+ it '#error? is false' do
39
+ expect(subject).to be_error
40
+ end
41
+
42
+ it '#success? is false' do
43
+ expect(subject).not_to be_success
44
+ end
45
+
46
+ it '#pending? is false' do
47
+ expect(subject).not_to be_pending
48
+ end
49
+ end
50
+
51
+ context 'when success' do
52
+ let(:status) { 'Success' }
53
+
54
+ it '#status is Success' do
55
+ expect(subject.status).to eq status
56
+ end
57
+
58
+ it '#error? is false' do
59
+ expect(subject).not_to be_error
60
+ end
61
+
62
+ it '#success? is true' do
63
+ expect(subject).to be_success
64
+ end
65
+
66
+ it '#pending? is false' do
67
+ expect(subject).not_to be_pending
68
+ end
69
+ end
70
+
71
+ context 'when pending' do
72
+ let(:status) { 'Pending' }
73
+
74
+ it '#status is Pending' do
75
+ expect(subject.status).to eq status
76
+ end
77
+
78
+ it '#error? is false' do
79
+ expect(subject).not_to be_error
80
+ end
81
+
82
+ it '#success? is false' do
83
+ expect(subject).not_to be_success
84
+ end
85
+
86
+ it '#pending? is true' do
87
+ expect(subject).to be_pending
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,19 @@
1
+ RSpec.describe SoapyBing::Soap::Response::SubmitGenerateReportResponse do
2
+ let(:request_id) { 'foobarbazqux' }
3
+ let(:response_hash) do
4
+ {
5
+ 'Envelope' => {
6
+ 'Body' => {
7
+ 'SubmitGenerateReportResponse' => {
8
+ 'ReportRequestId' => request_id
9
+ }
10
+ }
11
+ }
12
+ }
13
+ end
14
+ let(:subject) { described_class.new(response_hash) }
15
+
16
+ it '#extract_payload returns request id' do
17
+ expect(subject.extract_payload).to eq request_id
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ RSpec.describe SoapyBing::Soap::TemplateRenderer do
2
+ describe '::TEMPLATE_PATH' do
3
+ let(:files) { Dir.glob(File.join(described_class::TEMPLATE_PATH, '*.erb.xml')) }
4
+
5
+ it 'points to folder with *.erb.xml files' do
6
+ expect(files.size).to be > 1
7
+ end
8
+ end
9
+
10
+ describe '#render' do
11
+ let(:renderer) { described_class.new(greeting: 'Hello', target: 'World', provocation: '< &') }
12
+
13
+ before do
14
+ stub_const(
15
+ "#{described_class}::TEMPLATE_PATH",
16
+ File.join('spec', 'fixtures', 'soap_templates')
17
+ )
18
+ end
19
+
20
+ it 'returns text with interpolated variables' do
21
+ expect(renderer.render(:simple)).to eq "Hello, World!\n&lt; &amp;\n"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ require 'simplecov_setup'
2
+
3
+ require 'bundler/setup'
4
+ Bundler.require(:default, :development)
5
+
6
+ RSpec.configure do |config|
7
+ config.expect_with :rspec do |expectations|
8
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
9
+ end
10
+
11
+ config.mock_with :rspec do |mocks|
12
+ mocks.verify_partial_doubles = true
13
+ end
14
+
15
+ config.filter_run :focus
16
+ config.run_all_when_everything_filtered = true
17
+
18
+ config.disable_monkey_patching!
19
+ config.expose_dsl_globally = false
20
+
21
+ if config.files_to_run.one?
22
+ config.default_formatter = 'doc'
23
+ end
24
+
25
+ config.order = :random
26
+
27
+ Kernel.srand config.seed
28
+ end
29
+
30
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
31
+
32
+ require 'soapy_bing'
@@ -0,0 +1,3 @@
1
+ require 'dotenv'
2
+
3
+ Dotenv.load('.env.local', '.env')
@@ -0,0 +1,101 @@
1
+ require 'cgi'
2
+ require 'vcr'
3
+ require 'active_support/core_ext/hash/conversions'
4
+
5
+ VCR.configure do |c|
6
+ c.configure_rspec_metadata!
7
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
8
+ c.hook_into :webmock
9
+ c.default_cassette_options = { match_requests_on: %i(method uri body) }
10
+
11
+ c.filter_sensitive_data('bing-ads-oauth-client-id') { ENV['BING_ADS_OAUTH_CLIENT_ID'] }
12
+ c.filter_sensitive_data('bing-ads-oauth-client-secret') { ENV['BING_ADS_OAUTH_CLIENT_SECRET'] }
13
+ c.filter_sensitive_data('bing-ads-oauth-refresh-token') do
14
+ CGI.escape(ENV['BING_ADS_OAUTH_REFRESH_TOKEN'])
15
+ end
16
+ c.filter_sensitive_data('bing-ads-oauth-refresh-token') do |interaction|
17
+ if interaction.response.headers['Content-Type'].first == 'application/json'
18
+ JSON.parse(interaction.response.body)['refresh_token']
19
+ end
20
+ end
21
+ c.filter_sensitive_data('bing-ads-oauth-authentication-token') do |interaction|
22
+ if interaction.response.headers['Content-Type'].first == 'application/json'
23
+ JSON.parse(interaction.response.body)['access_token']
24
+ end
25
+ end
26
+ c.filter_sensitive_data('bing-ads-oauth-user-id') do |interaction|
27
+ if interaction.response.headers['Content-Type'].first == 'application/json'
28
+ JSON.parse(interaction.response.body)['user_id']
29
+ end
30
+ end
31
+ c.filter_sensitive_data('bing-ads-developer-token') { ENV['BING_ADS_DEVELOPER_TOKEN'] }
32
+ c.filter_sensitive_data('bing-ads-account-id') { ENV['BING_ADS_ACCOUNT_ID'] }
33
+ c.filter_sensitive_data('bing-ads-customer-id') { ENV['BING_ADS_CUSTOMER_ID'] }
34
+
35
+ c.filter_sensitive_data('bing-ads-report-tracking-id') do |interaction|
36
+ if interaction.response.headers['Content-Type'].first == 'text/xml; charset=utf-8'
37
+ Hash.from_xml(interaction.response.body)['Envelope']['Header']['TrackingId']
38
+ end
39
+ end
40
+ c.filter_sensitive_data('bing-ads-report-request-id') do |interaction|
41
+ if interaction.response.headers['Content-Type'].first == 'text/xml; charset=utf-8'
42
+ body = Hash.from_xml(interaction.response.body)['Envelope']['Body']
43
+ if body['SubmitGenerateReportResponse']
44
+ body['SubmitGenerateReportResponse']['ReportRequestId']
45
+ end
46
+ end
47
+ end
48
+ c.filter_sensitive_data('bing-ads-report-request-id') do |interaction|
49
+ if interaction.response.headers['Content-Type'].first == 'text/xml; charset=utf-8'
50
+ body = Hash.from_xml(interaction.request.body)['Envelope']['Body']
51
+ if body['PollGenerateReportRequest']
52
+ body['PollGenerateReportRequest']['ReportRequestId']
53
+ end
54
+ end
55
+ end
56
+ c.filter_sensitive_data('bing-ads-oauth-authentication-token') do |interaction|
57
+ if interaction.response.headers['Content-Type'].first == 'text/xml; charset=utf-8'
58
+ Hash.from_xml(interaction.request.body)['Envelope']['Header']['AuthenticationToken']
59
+ end
60
+ end
61
+ c.filter_sensitive_data('bing-ads-report-download-id') do |interaction|
62
+ if interaction.request.uri =~ %r{https://(.*)\?q=(.*)}
63
+ Regexp.last_match(2)
64
+ end
65
+ end
66
+ c.filter_sensitive_data('bing-ads-report-download-id') do |interaction|
67
+ if interaction.response.headers['Content-Type'].first == 'text/xml; charset=utf-8'
68
+ response = Hash.from_xml(interaction.response.body)
69
+ report = response['Envelope']['Body']['PollGenerateReportResponse']
70
+ if report && report ['ReportRequestStatus']
71
+ if report['ReportRequestStatus']['ReportDownloadUrl'] =~ %r{https://(.*)\?q=(.*)}
72
+ Regexp.last_match(2)
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ c.before_record do |interaction|
79
+ # auto-generate report payload fixtures
80
+ # spec/fixtures/reports/campaign_performance_report.json
81
+ # spec/fixtures/reports/campaing_performance_report.csv
82
+ if interaction.response.headers['Content-Type'].first == 'application/x-zip-compressed'
83
+ # refactor zip into module
84
+ csv_data = Zip::InputStream.open(StringIO.new(interaction.response.body)) do |archive_io|
85
+ file_io = archive_io.get_next_entry.get_input_stream
86
+ file_io.read
87
+ end
88
+
89
+ fixtures_dir = File.join('spec', 'fixtures', 'reports')
90
+ File.open(File.join(fixtures_dir, 'campaign_performance_report.json'), 'wb') do |file|
91
+ parser = SoapyBing::Ads::Reports::Parsers::CSVParser.new(csv_data)
92
+ file.write(JSON.pretty_generate(parser.rows))
93
+ end
94
+ File.open(File.join(fixtures_dir, 'campaign_performance_report.csv'), 'wb') do |file|
95
+ file.write(csv_data)
96
+ end
97
+ end
98
+ end
99
+
100
+ c.ignore_hosts 'codeclimate.com' # allow codeclimate-test-reporter to phone home
101
+ end