google-adwords-api 0.2.0 → 0.2.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.
data/ChangeLog CHANGED
@@ -1,3 +1,7 @@
1
+ 0.2.1:
2
+ - Changes for future OAuth support.
3
+ - Require google-ads-common 0.5.0 or later from now on.
4
+
1
5
  0.2.0:
2
6
  - Better support for UTF reports in download extensions.
3
7
  - Require google-ads-common 0.4.0 or later from now on.
data/README CHANGED
@@ -30,7 +30,7 @@ which is installed automatically as a dependency.
30
30
  The following gem libraries are required:
31
31
  - soap4r v1.5.8
32
32
  - httpclient v2.1.2 or greater
33
- - google-ads-common v0.4.0 or later.
33
+ - google-ads-common v0.5.0 or later.
34
34
 
35
35
 
36
36
  == 2 - Using the client library:
data/Rakefile CHANGED
@@ -25,10 +25,10 @@ $:.unshift File.join(File.dirname(__FILE__), 'lib')
25
25
 
26
26
  require 'rubygems'
27
27
  gem 'google-ads-common'
28
- require 'ads_common/build/soap4r_generator'
29
28
  require 'ads_common/config'
30
29
  require 'adwords_api/api_config'
31
30
  require 'adwords_api/extensions'
31
+ require 'adwords_api/soap4r/soap4r_generator'
32
32
 
33
33
  # Configure some constants and built-in tasks
34
34
  $PROJECT_NAME = 'adwords_api'
@@ -36,7 +36,7 @@ $GEM_NAME = 'google-adwords-api'
36
36
  $API_NAME = 'AdWords API'
37
37
  $CURRENT_VERSION = AdwordsApi::ApiConfig::CLIENT_LIB_VERSION
38
38
  $PRODDIR = 'adwords_api'
39
- $ADS_COMMON_VERSION = '~> 0.4.0'
39
+ $ADS_COMMON_VERSION = '~> 0.5.0'
40
40
 
41
41
  # Configure module / namespace
42
42
  $MODULE = AdwordsApi
@@ -63,7 +63,7 @@ $GEM_HOMEPAGE = 'http://code.google.com/p/google-api-ads-ruby/'
63
63
  module Generator
64
64
 
65
65
  class << Generator
66
- include AdsCommon::Build::Soap4rGenerator
66
+ include AdwordsApi::Soap4r::Soap4rGenerator
67
67
  end
68
68
 
69
69
  def self.api_config
@@ -114,4 +114,4 @@ end
114
114
 
115
115
 
116
116
  # Import tasks in common file
117
- require 'ads_common/build/rake_common'
117
+ require 'adwords_api/soap4r/rake_common'
data/adwords_api.yml CHANGED
@@ -2,17 +2,34 @@
2
2
  # This is an example configuration file for the AdWords API client library.
3
3
  # Please fill in the required fields, and copy it over to your home directory.
4
4
  :authentication:
5
+ # Authentication method, methods currently supported: OAuth, ClientLogin.
5
6
  :method: ClientLogin
6
- :developer_token: INSERT_YOUR_DEVELOPER_TOKEN_HERE
7
- :user_agent: INSERT_YOUR_USER_AGENT_HERE
7
+
8
+ # Auth parameters for OAuth method.
9
+ # Set the OAuth consumer key and secret. Anonymous values can be used for
10
+ # testing, and real values can be obtained by registering your application:
11
+ # http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html
12
+ #:oauth_consumer_key: anonymous
13
+ #:oauth_consumer_secret: anonymous
14
+ # If you manage or store access token manually, you can specify it here.
15
+ #:oauth_token: INSERT_OAUTH_TOKEN_HERE
16
+ # If you need to change signature method, specify it here.
17
+ #:oauth_signature_method: HMAC-SHA1
18
+ # Token secret for HMAC-SHA1 method.
19
+ #:oauth_token_secret: INSERT_OAUTH_TOKEN_SECRET_HERE
20
+
21
+ # Auth parameters for ClientLogin method.
8
22
  :password: INSERT_YOUR_PASSWORD_HERE
9
23
  :email: INSERT_YOUR_LOGIN_EMAIL_HERE
10
- :client_email: INSERT_YOUR_CLIENT_EMAIL_HERE
11
- # If you would like to manage your auth tokens manually, use the 'auth_token'
12
- # property.
24
+ # To manage your auth tokens manually, use the 'auth_token' property.
13
25
  #:auth_token: INSERT_AUTH_TOKEN_HERE
26
+
27
+ # Other parameters.
28
+ :developer_token: INSERT_YOUR_DEVELOPER_TOKEN_HERE
29
+ :client_email: INSERT_YOUR_CLIENT_EMAIL_HERE
30
+ :user_agent: INSERT_YOUR_USER_AGENT_HERE
14
31
  :service:
15
- :environment: Production
32
+ :environment: SANDBOX
16
33
  :connection:
17
34
  :strict_ssl_verification: false
18
35
  # If your proxy connection requires authentication, make sure to include it in
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Author:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # This example illustrates how to handle 2 factor authorization errors.
21
+
22
+ require 'rubygems'
23
+ gem 'google-adwords-api'
24
+ require 'adwords_api'
25
+
26
+ API_VERSION = :v201101
27
+
28
+ def handle_second_factor()
29
+ # Set up credentials with an account that has 2Factor enabled.
30
+ config = {
31
+ :authentication => {
32
+ :method => 'ClientLogin',
33
+ :email => '2steptester@gmail.com',
34
+ :password => 'testaccount',
35
+ :user_agent => 'Ruby 2 Factor Sample'
36
+ },
37
+ :service => {
38
+ :environment => 'PRODUCTION'
39
+ }
40
+ }
41
+ adwords = AdwordsApi::Api.new(config)
42
+
43
+ # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
44
+ # the configuration file or provide your own logger:
45
+ # adwords.logger = Logger.new('adwords_xml.log')
46
+
47
+ begin
48
+ # Forcing library to request authorization token.
49
+ auth_token = adwords.authorize()
50
+ puts 'Successfully retrieved the token.'
51
+
52
+ # Second factor error is one of AuthErrors.
53
+ rescue AdsCommon::Errors::AuthError => e
54
+ puts "Authorization failed with message:"
55
+ puts "\t%s" % e.message
56
+ # Checking 'Info' field for particular auth error type.
57
+ if e.info and e.info.casecmp('InvalidSecondFactor') == 0
58
+ puts "The user has enabled two factor authentication in this account." +
59
+ " Please use OAuth authentication method or have the user generate an" +
60
+ " application-specific password to make calls against the AdWords" +
61
+ " API. See \n" +
62
+ " http://adwordsapi.blogspot.com/2011/02/authentication-changes-with" +
63
+ "-2-step.html\n" +
64
+ "for more details."
65
+ end
66
+ end
67
+ end
68
+
69
+ if __FILE__ == $0
70
+ begin
71
+ handle_second_factor()
72
+
73
+ # Connection error. Likely transitory.
74
+ rescue Errno::ECONNRESET, SOAP::HTTPStreamError, SocketError => e
75
+ puts 'Connection Error: %s' % e
76
+ puts 'Source: %s' % e.backtrace.first
77
+
78
+ # API Error.
79
+ rescue AdwordsApi::Errors::ApiException => e
80
+ puts 'API Exception caught.'
81
+ puts 'Message: %s' % e.message
82
+ puts 'Code: %d' % e.code if e.code
83
+ puts 'Trigger: %s' % e.trigger if e.trigger
84
+ puts 'Errors:'
85
+ if e.errors
86
+ e.errors.each_with_index do |error, index|
87
+ puts ' %d. Error type is %s. Fields:' % [index + 1, error[:xsi_type]]
88
+ error.each_pair do |field, value|
89
+ if field != :xsi_type
90
+ puts ' %s: %s' % [field, value]
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Author:: api.dklimkin@gmail.com (Danial Klimkin)
4
+ #
5
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
6
+ #
7
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16
+ # implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ # This example illustrates how to use OAuth authentication method.
21
+ #
22
+ # Tags: CampaignService.get
23
+
24
+ require 'rubygems'
25
+ gem 'google-adwords-api'
26
+ require 'adwords_api'
27
+
28
+ API_VERSION = :v201101
29
+ MAX_RETRIES = 3
30
+
31
+ def oauth_handling()
32
+ # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
33
+ # when called without parameters.
34
+ adwords = AdwordsApi::Api.new
35
+
36
+ # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
37
+ # the configuration file or provide your own logger:
38
+ # adwords.logger = Logger.new('adwords_xml.log')
39
+
40
+ campaign_srv = adwords.service(:CampaignService, API_VERSION)
41
+
42
+ # Get all the campaigns for this account; empty selector.
43
+ selector = {
44
+ :fields => ['Id', 'Name', 'Status'],
45
+ :ordering => [{:field => 'Name', :sort_order => 'ASCENDING'}]
46
+ }
47
+
48
+ retry_count = 0
49
+
50
+ begin
51
+ response = campaign_srv.get(selector)
52
+ rescue AdsCommon::Errors::OAuthVerificationRequired => e
53
+ if retry_count < MAX_RETRIES
54
+ puts "Hit Auth error, please navigate to URL:\n\t%s" % e.oauth_url
55
+ print 'log in and type the verification code: '
56
+ verification_code = gets.chomp
57
+ adwords.credential_handler.set_credential(
58
+ :oauth_verification_code, verification_code)
59
+ retry_count += 1
60
+ retry
61
+ else
62
+ raise AdsCommon::Errors::AuthError, 'Failed to authenticate.'
63
+ end
64
+ end
65
+
66
+ if response and response[:entries]
67
+ campaigns = response[:entries]
68
+ campaigns.each do |campaign|
69
+ puts "Campaign name is \"#{campaign[:name]}\", id is #{campaign[:id]} " +
70
+ "and status is \"#{campaign[:status]}\"."
71
+ end
72
+ else
73
+ puts "No campaigns were found."
74
+ end
75
+ end
76
+
77
+ if __FILE__ == $0
78
+ begin
79
+ oauth_handling()
80
+
81
+ # Connection error. Likely transitory.
82
+ rescue Errno::ECONNRESET, SOAP::HTTPStreamError, SocketError => e
83
+ puts 'Connection Error: %s' % e
84
+ puts 'Source: %s' % e.backtrace.first
85
+
86
+ # API Error.
87
+ rescue AdwordsApi::Errors::ApiException => e
88
+ puts 'API Exception caught.'
89
+ puts 'Message: %s' % e.message
90
+ puts 'Code: %d' % e.code if e.code
91
+ puts 'Trigger: %s' % e.trigger if e.trigger
92
+ puts 'Errors:'
93
+ if e.errors
94
+ e.errors.each_with_index do |error, index|
95
+ puts ' %d. Error type is %s. Fields:' % [index + 1, error[:xsi_type]]
96
+ error.each_pair do |field, value|
97
+ if field != :xsi_type
98
+ puts ' %s: %s' % [field, value]
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
data/lib/adwords_api.rb CHANGED
@@ -22,22 +22,21 @@
22
22
 
23
23
  require 'rubygems'
24
24
  gem 'soap4r', '=1.5.8'
25
- gem 'google-ads-common', '~>0.4.0'
25
+ gem 'google-ads-common', '~>0.5.0'
26
26
  require 'thread'
27
27
  require 'uri'
28
- require 'ads_common/soap4r_patches'
29
28
  require 'ads_common/api'
30
- require 'ads_common/config'
31
29
  require 'ads_common/auth/client_login_handler'
32
- require 'ads_common/soap4r_headers/nested_header_handler'
33
- require 'ads_common/soap4r_headers/single_header_handler'
34
- require 'ads_common/soap4r_logger'
35
30
  require 'adwords_api/auth/v13_login_handler'
36
31
  require 'adwords_api/errors'
37
32
  require 'adwords_api/api_config'
38
33
  require 'adwords_api/extensions'
39
- require 'adwords_api/soap4r_response_handler'
40
34
  require 'adwords_api/credential_handler'
35
+ require 'adwords_api/nested_header_handler'
36
+ require 'adwords_api/single_header_handler'
37
+ require 'adwords_api/soap4r/soap4r_logger'
38
+ require 'adwords_api/soap4r/soap4r_patches'
39
+ require 'adwords_api/soap4r/soap4r_response_handler'
41
40
  require 'adwords_api/utils'
42
41
 
43
42
  # Main namespace for all the client library's modules and classes.
@@ -55,11 +54,6 @@ module AdwordsApi
55
54
  # Number of units spent in total, via this API object
56
55
  attr_accessor :total_units
57
56
 
58
- # Accessor for client login handler, so that we can access the token.
59
- attr_accessor :client_login_handler
60
-
61
- public
62
-
63
57
  # Getter for the API service configurations
64
58
  def api_config
65
59
  AdwordsApi::ApiConfig
@@ -68,7 +62,7 @@ module AdwordsApi
68
62
  # Auxiliary method to create a new Soap4rResponseHandler, of the type we
69
63
  # want to use for AdWords logging
70
64
  def create_callback_handler
71
- AdwordsApi::Soap4rResponseHandler.new(self)
65
+ AdwordsApi::Soap4r::Soap4rResponseHandler.new(self)
72
66
  end
73
67
 
74
68
  # Retrieve single NestedHeaderHandler for v20xx and one SingleHeaderHandler
@@ -79,27 +73,33 @@ module AdwordsApi
79
73
  # handle authentication
80
74
  # - header_list: the list of headers to be handled
81
75
  # - version: intended API version
82
- # - wrapper: wrapper object for the service being handled
76
+ # - namespace: namespace to use as default for body
83
77
  #
84
78
  # Returns:
85
79
  # - a list of SOAP header handlers; one per provided header
86
80
  #
87
- def soap_header_handlers(auth_handler, header_list, version, wrapper)
81
+ def soap_header_handlers(auth_handler, header_list, version, namespace)
88
82
  if version == :v13
89
- header_handlers = []
90
- header_list.each do |header|
91
- header_handlers << AdsCommon::Soap4rHeaders::SingleHeaderHandler.new(
92
- @credential_handler, auth_handler, header, nil, version)
83
+ header_handlers = header_list.map do |header|
84
+ AdwordsApi::SingleHeaderHandler.new(@credential_handler, auth_handler,
85
+ header, nil, version)
93
86
  end
94
87
  return header_handlers
95
88
  else
96
- ns =
97
- api_config.headers_config[:HEADER_NAMESPACE_PREAMBLE] + version.to_s
98
- top_ns = wrapper.namespace
99
- return [AdsCommon::Soap4rHeaders::NestedHeaderHandler.new(
100
- @credential_handler, auth_handler,
101
- api_config.headers_config[:REQUEST_HEADER],
102
- top_ns, ns, version)]
89
+ auth_method = @config.read('authentication.method',
90
+ 'ClientLogin').to_s.upcase.to_sym
91
+ handlers = case auth_method
92
+ when :CLIENTLOGIN
93
+ ns = api_config.headers_config[:HEADER_NAMESPACE_PREAMBLE] +
94
+ version.to_s
95
+ [AdwordsApi::NestedHeaderHandler.new(@credential_handler,
96
+ auth_handler, api_config.headers_config[:REQUEST_HEADER],
97
+ namespace, ns, version)]
98
+ when :OAUTH
99
+ raise NotImplementedError, 'OAuth authentication method is not ' +
100
+ 'supported for Soap4r backend.'
101
+ end
102
+ return handlers
103
103
  end
104
104
  end
105
105
 
@@ -107,12 +107,34 @@ module AdwordsApi
107
107
  def initialize(provided_config = nil)
108
108
  super(provided_config)
109
109
  @credential_handler = AdwordsApi::CredentialHandler.new(@config)
110
- @drivers = Hash.new
110
+ @drivers = {}
111
111
  @total_units = 0
112
112
  @last_units = 0
113
113
  @mutex = Mutex.new
114
114
  end
115
115
 
116
+ # Accessor for client login handler, so that we can access the token.
117
+ # This is a temporary solution for v13 and for v2009+ until full OAuth
118
+ # support is available.
119
+ def client_login_handler()
120
+ if @client_login_handler.nil?
121
+ auth_method_str = @config.read('authentication.method', 'ClientLogin')
122
+ auth_method = auth_method_str.to_s.upcase.to_sym
123
+ environment = @config.read('service.environment')
124
+ if auth_method == :CLIENTLOGIN
125
+ # We use client login, so we can reuse general AuthHandler.
126
+ @client_login_handler = get_auth_handler(environment, nil)
127
+ else
128
+ # We are using OAuth or something else and need to generate a handler.
129
+ auth_server = api_config.auth_server(environment)
130
+ @client_login_handler = AdsCommon::Auth::ClientLoginHandler.new(
131
+ @config, auth_server,
132
+ api_config.headers_config[:LOGIN_SERVICE_NAME])
133
+ end
134
+ end
135
+ return @client_login_handler
136
+ end
137
+
116
138
  # Helper method to provide a simple way of doing an MCC-level operation
117
139
  # without the need to change credentials. Executes a block of code as an
118
140
  # MCC-level operation and/or returns the current status of the property.
@@ -219,28 +241,29 @@ module AdwordsApi
219
241
 
220
242
  private
221
243
 
244
+ # Overrides AdsCommon::Api.get_auth_handler to allow version-specific
245
+ # handlers.
246
+ def get_auth_handler(environment, version = nil)
247
+ if @auth_handler.nil?
248
+ @auth_handler = create_auth_handler(environment, version)
249
+ end
250
+ return @auth_handler
251
+ end
252
+
222
253
  # Creates an appropriate authentication handler for each service (reuses the
223
254
  # ClientLogin one to avoid generating multiple tokens unnecessarily).
224
255
  #
225
256
  # Args:
226
- # - version: intended API version
227
257
  # - environment: the current working environment (production, sandbox, etc.)
258
+ # - version: intended API version
228
259
  #
229
260
  # Returns:
230
261
  # - auth handler
231
262
  #
232
- def create_auth_handler(environment, version)
233
- if version == :v13
234
- return AdwordsApi::Auth::V13LoginHandler.new
235
- else
236
- if @client_login_handler.nil?
237
- auth_server = api_config.auth_server(environment)
238
- @client_login_handler =
239
- AdsCommon::Auth::ClientLoginHandler.new(config, auth_server,
240
- api_config.headers_config[:LOGIN_SERVICE_NAME])
241
- end
242
- return @client_login_handler
243
- end
263
+ def create_auth_handler(environment, version = nil)
264
+ return (version == :v13) ?
265
+ AdwordsApi::Auth::V13LoginHandler.new :
266
+ super(environment)
244
267
  end
245
268
 
246
269
  # Handle loading of a single service.
@@ -271,12 +294,12 @@ module AdwordsApi
271
294
  wrapper_class = api_config.wrapper_name(version, service)
272
295
  wrapper = eval("#{wrapper_class}.new(driver, self)")
273
296
 
274
- auth_handler = create_auth_handler(environment, version)
297
+ auth_handler = get_auth_handler(environment, version)
275
298
  header_list =
276
299
  auth_handler.header_list(@credential_handler.credentials(version))
277
300
 
278
301
  soap_handlers = soap_header_handlers(auth_handler, header_list, version,
279
- wrapper)
302
+ wrapper.namespace)
280
303
 
281
304
  soap_handlers.each do |handler|
282
305
  driver.headerhandler << handler
@@ -285,7 +308,8 @@ module AdwordsApi
285
308
  # Add response handler to this driver for API unit usage processing.
286
309
  driver.callbackhandler = create_callback_handler
287
310
  # Plug the wiredump to our XML logger.
288
- driver.wiredump_dev = AdsCommon::Soap4rLogger.new(@logger, Logger::DEBUG)
311
+ driver.wiredump_dev =
312
+ AdwordsApi::Soap4r::Soap4rLogger.new(@logger, Logger::DEBUG)
289
313
  driver.options['protocol.http.ssl_config.verify_mode'] = nil
290
314
  proxy = config.read('connection.proxy')
291
315
  driver.options['protocol.http.proxy'] = proxy if proxy