google-adwords-api 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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