google-ads-common 0.2.0

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/README ADDED
@@ -0,0 +1,68 @@
1
+ = Google AdsCommon Library
2
+
3
+ This gem is a dependency for the new generation of Ruby Google Ads client
4
+ libraries. It contains common code shared among all of these libraries, such as
5
+ authentication, SOAP stub generation, error handling, logging, etc.
6
+
7
+ This is an early preview release, so a lot of things are still missing!
8
+
9
+
10
+ = Docs for Users
11
+
12
+ == 1 - Installation:
13
+
14
+ google-ads-common is a Ruby gem. See http://docs.rubygems.org/read/book/1
15
+
16
+ Install it using the gem install command.
17
+ $ gem install --local google-ads-common-VERSION.gem
18
+
19
+ The following gem libraries are required:
20
+ - soap4r v1.5.8 (only for soap4r-based libraries);
21
+ - savon v0.8.5 or greater (only for savon-enabled libraries);
22
+ - httpclient v2.1.2 or greater.
23
+
24
+ = Docs for Developers
25
+
26
+ == 1 - Directory Structure
27
+
28
+ - +lib/ads_common/+: Contains the bulk of the library.
29
+ - +auth/+: Contains classes that can handle different authentication methods.
30
+ Currently only supports ClientLogin.
31
+ - +build/+: Contains classes that handle code generation, packaging and other
32
+ build-time tasks for client libraries (not for google-ads-common).
33
+ - +savon_headers/+: Contains classes that handle header injection into savon
34
+ requests.
35
+ - +soap4r_headers/+: Contains classes that handle header injection into soap4r
36
+ requests.
37
+ - +test/+: Contains the unit tests for the library.
38
+
39
+ == 2 - Commands
40
+
41
+ $ rake package
42
+ to package the gem and create a release
43
+
44
+ $ rake test
45
+ to run unit tests on the library
46
+
47
+
48
+ = Copyright/License Info
49
+
50
+ Copyright 2010, Google Inc. All Rights Reserved.
51
+
52
+ Licensed under the Apache License, Version 2.0 (the "License");
53
+ you may not use this file except in compliance with the License.
54
+ You may obtain a copy of the License at
55
+
56
+ http://www.apache.org/licenses/LICENSE-2.0
57
+
58
+ Unless required by applicable law or agreed to in writing, software
59
+ distributed under the License is distributed on an "AS IS" BASIS,
60
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
61
+ See the License for the specific language governing permissions and
62
+ limitations under the License.
63
+
64
+
65
+ == Authors
66
+
67
+ Sérgio Gomes (api.sgomes@gmail.com)
68
+ Danial Klimkin (api.dklimkin@gmail.com)
data/Rakefile ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
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
+ # Rakefile for the ads_common package.
21
+
22
+ require 'rubygems'
23
+ gem 'rake'
24
+ require 'rake/gempackagetask'
25
+ require 'rake/rdoctask'
26
+ require 'rake/clean'
27
+ require 'rake/testtask'
28
+ require 'lib/ads_common/api_config'
29
+
30
+ $WSDLDIR = 'wsdl'
31
+ $LIBDIR = 'lib'
32
+ $DOCDIR = 'doc'
33
+ $TESTDIR = 'test'
34
+
35
+ $PROJECT_NAME = 'ads_common'
36
+ $GEM_NAME = 'google-ads-common'
37
+
38
+ $CURRENT_VERSION = AdsCommon::ApiConfig::ADS_COMMON_VERSION
39
+ $PKG_VERSION = ENV['REL'] ? ENV['REL'] : $CURRENT_VERSION
40
+
41
+ # Configure gem details
42
+ $GEM_SUMMARY = "Common code for Google Ads APIs."
43
+ $GEM_DESCRIPTION = "#{$PROJECT_NAME} provides essential utilities shared by " +
44
+ "all Ads Ruby client libraries."
45
+ $GEM_AUTHORS = ['Sergio Gomes', 'Danial Klimkin']
46
+ $GEM_EMAIL = 'api.sgomes@gmail.com'
47
+ $GEM_HOMEPAGE = 'http://code.google.com/p/google-api-adwords-ruby/'
48
+
49
+ # ====================================================================
50
+ # Default task - call package
51
+ task :default => [:package]
52
+
53
+ # ====================================================================
54
+ # Create a task to build the RDOC documentation tree.
55
+ Rake::RDocTask.new("rdoc") do |rdoc|
56
+ # Try to use SDoc to generate the docs
57
+ begin
58
+ require 'sdoc'
59
+ rdoc.options << '--fmt' << 'shtml'
60
+ rdoc.template = 'direct'
61
+ rescue LoadError
62
+ # Do nothing, give up on SDoc and continue with whatever is the default.
63
+ end
64
+ rdoc.rdoc_dir = $DOCDIR
65
+ rdoc.title = "#{$PROJECT_NAME} -- Common code for Google Ads APIs"
66
+ rdoc.main = 'README'
67
+ rdoc.rdoc_files.include('README', 'COPYING', 'ChangeLog')
68
+ rdoc.rdoc_files.include("#{$LIBDIR}/**/*.rb")
69
+ end
70
+
71
+ # ====================================================================
72
+ # Create a task to perform the unit testing.
73
+ Rake::TestTask.new("test") do |test|
74
+ test.libs << $TESTDIR
75
+ test.pattern = "#{$TESTDIR}/**/test_*.rb"
76
+ test.verbose = true
77
+ end
78
+
79
+ # ====================================================================
80
+ # Create a task that will package the Rake software into distributable
81
+ # gem files.
82
+ PKG_FILES = FileList[
83
+ '*.*',
84
+ 'Rakefile',
85
+ "#{$LIBDIR}/**/*.rb",
86
+ "#{$DOCDIR}/**/*.*",
87
+ "#{$TESTDIR}/**/*.*"
88
+ ]
89
+
90
+ PKG_FILES.exclude(/\._/)
91
+
92
+ if ! defined?(Gem)
93
+ puts "Package Target requires RubyGems"
94
+ else
95
+ spec = Gem::Specification.new do |s|
96
+
97
+ # Basic information
98
+ s.name = $GEM_NAME
99
+ s.version = $PKG_VERSION
100
+ s.summary = $GEM_SUMMARY
101
+ s.description = $GEM_DESCRIPTION
102
+
103
+ # Files and dependencies
104
+ s.files = PKG_FILES.to_a
105
+ s.require_path = $LIBDIR
106
+ s.add_dependency('soap4r', '= 1.5.8')
107
+ s.add_dependency('httpclient', '>= 2.1.5.2')
108
+ s.add_dependency('httpi', '~>0.7.9')
109
+
110
+ # RDoc information
111
+ s.has_rdoc = true
112
+ s.extra_rdoc_files = ['README']
113
+ s.rdoc_options << '--main' << 'README'
114
+
115
+ # Metadata
116
+ s.authors = $GEM_AUTHORS
117
+ s.email = $GEM_EMAIL
118
+ s.homepage = $GEM_HOMEPAGE
119
+ s.rubyforge_project = $GEM_NAME
120
+ s.requirements << 'soap4r v1.5.8'
121
+ s.requirements << 'httpclient v2.1.5.2 or greater'
122
+ s.requirements << 'httpi v0.7.9 or greater'
123
+ end
124
+
125
+ Rake::GemPackageTask.new(spec) do |t|
126
+ t.need_tar = true
127
+ end
128
+ end
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ #
5
+ # Copyright:: Copyright 2010, 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
+ # Generic Api class, to be inherited from and extended by specific APIs.
21
+
22
+ require 'ads_common/errors'
23
+ require 'ads_common/auth/base_handler'
24
+ require 'ads_common/auth/client_login_handler'
25
+ require 'ads_common/soap4r_headers/nested_header_handler'
26
+ require 'ads_common/soap4r_headers/single_header_handler'
27
+ require 'ads_common/soap4r_response_handler'
28
+
29
+ module AdsCommon
30
+ class Api
31
+
32
+ # Auxiliary method to create a new Soap4rResponseHandler
33
+ # Should be redefined in inheriting classes, if planning to do specific
34
+ # logging.
35
+ def create_handler
36
+ AdsCommon::Soap4rResponseHandler.new(self)
37
+ end
38
+
39
+ # Logger object used for logging request info
40
+ attr_reader :request_logger
41
+
42
+ # Logger object used for logging SOAP XML
43
+ attr_reader :xml_logger
44
+
45
+ # Methods that return the service configuration and the client library
46
+ # configuration. They need to be redefined in subclasses.
47
+ attr_reader :api_config, :config
48
+
49
+ # Credential handler objects used for authentication
50
+ attr_reader :credential_handler
51
+
52
+ # The configuration for this Api object
53
+ attr_reader :config
54
+
55
+ # Obtain an API service, given a version and its name.
56
+ #
57
+ # Args:
58
+ # - name: name for the intended service
59
+ # - version: intended API version.
60
+ #
61
+ # Returns:
62
+ # - the service wrapper for the intended service.
63
+ #
64
+ def service(name, version = nil)
65
+ version = api_config.default_version if version == nil
66
+ # Check if version exists
67
+ if !api_config.versions.include?(version.to_sym)
68
+ if version.is_a? Integer
69
+ raise AdsCommon::Errors::Error, "Unknown version '#{version}'. " +
70
+ "Please note that version numbers should be symbols; check the" +
71
+ "Readme or the included examples for more information."
72
+ else
73
+ raise AdsCommon::Errors::Error, "Unknown version #{version}"
74
+ end
75
+ end
76
+ version = version.to_sym
77
+ name = name.to_sym
78
+ environment = config.read('service.environment')
79
+ # Check if the current environment supports the requested version
80
+ if !api_config.environment_has_version(environment, version)
81
+ raise AdsCommon::Errors::Error,
82
+ "Environment #{environment} does not support version #{version}"
83
+ end
84
+ # Check if the specified version has the requested service
85
+ if !api_config.version_has_service(version, name)
86
+ raise AdsCommon::Errors::Error, "Version #{version} does not contain " +
87
+ "service #{name}"
88
+ end
89
+ prepare_driver(version, name)
90
+ return @wrappers[[version, name]]
91
+ end
92
+
93
+ # Retrieve the SOAP header handlers to plug into the drivers. Needs to
94
+ # be implemented on the specific API, because of the different types of
95
+ # SOAP headers (optional parameter specifying API version).
96
+ #
97
+ # Args:
98
+ # - auth_handler: instance of an AdsCommon::Auth::BaseHandler subclass to
99
+ # handle authentication
100
+ # - header_list: the list of headers to be handled
101
+ # - version: intended API version
102
+ # - wrapper: wrapper object for the service being handled
103
+ #
104
+ # Returns:
105
+ # - a list of SOAP header handlers; one per provided header
106
+ #
107
+ def soap_header_handlers(auth_handler, header_list, version, wrapper = nil)
108
+ end
109
+
110
+ private
111
+
112
+ # Auxiliary method to create an authentication handler. Needs to be
113
+ # implemented on the specific API because of the different nesting models
114
+ # used in different APIs and API versions.
115
+ #
116
+ # Args:
117
+ # - version: intended API version
118
+ # - environment: the current working environment (production, sandbox, etc.)
119
+ #
120
+ # Returns:
121
+ # - auth handler
122
+ #
123
+ def create_auth_handler(version = nil, environment = nil)
124
+ end
125
+
126
+ # Handle loading of a single service.
127
+ # Creates the driver, sets up handlers and loggers, declares the appropriate
128
+ # wrapper class and creates an instance of it.
129
+ #
130
+ # Args:
131
+ # - version: intended API version. Must be an integer.
132
+ # - service: name for the intended service
133
+ #
134
+ # Returns:
135
+ # - the driver for the service
136
+ # - the simplified wrapper generated for the driver
137
+ #
138
+ def prepare_driver(version, service)
139
+ version = version.to_sym
140
+ service = service.to_sym
141
+ environment = config.read('service.environment')
142
+ api_config.do_require(version, service)
143
+ endpoint = api_config.endpoint(environment, version, service)
144
+ # Set endpoint if not using the default
145
+ if endpoint.nil? then
146
+ driver = eval("#{api_config.interface_name(version, service)}.new")
147
+ else
148
+ endpoint_url = endpoint.to_s + service.to_s
149
+ driver = eval("#{api_config.interface_name(version, service)}.new" +
150
+ "(\"#{endpoint_url}\")")
151
+ end
152
+
153
+ # Create an instance of the wrapper class for this service.
154
+ wrapper_class = api_config.wrapper_name(version, service)
155
+ wrapper = eval("#{wrapper_class}.new(driver, self)")
156
+
157
+ auth_handler = create_auth_handler(version, environment)
158
+ header_list =
159
+ auth_handler.header_list(@credential_handler.credentials(version))
160
+
161
+ soap_handlers = soap_header_handlers(auth_handler, header_list, version,
162
+ wrapper)
163
+
164
+ soap_handlers.each do |handler|
165
+ driver.headerhandler << handler
166
+ end
167
+
168
+ # Add response handler to this driver for API unit usage processing.
169
+ driver.callbackhandler = create_callback_handler
170
+ # Plug the wiredump to our XML logger
171
+ driver.wiredump_dev = xml_logger
172
+ driver.options['protocol.http.ssl_config.verify_mode'] = nil
173
+ proxy = config.read('connection.proxy')
174
+ if proxy
175
+ driver.options['protocol.http.proxy'] = proxy
176
+ end
177
+
178
+ @drivers[[version, service]] = driver
179
+ @wrappers[[version, service]] = wrapper
180
+ return driver, wrapper
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Authors:: api.sgomes@gmail.com (Sérgio Gomes)
4
+ # api.dklimkin@gmail.com (Danial Klimkin)
5
+ #
6
+ # Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
7
+ #
8
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17
+ # implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ # Helper methods for loading and managing services for an API. Defines methods
22
+ # to be included as class modules in a service class for a specific API.
23
+
24
+ module AdsCommon
25
+
26
+ # Contains helper methods for loading and managing the available services.
27
+ # This module is meant to be imported into API-specific modules.
28
+ module ApiConfig
29
+ ADS_COMMON_VERSION = '0.2.0'
30
+
31
+ # Get the available API versions.
32
+ #
33
+ # Returns:
34
+ # List of versions available (as integers)
35
+ #
36
+ def versions
37
+ service_config.keys
38
+ end
39
+
40
+ # Get the latest API version.
41
+ #
42
+ # Returns:
43
+ # Latest version (as an integer)
44
+ #
45
+ def latest_version
46
+ service_config.keys.select { |service| service.is_a? Integer }.max
47
+ end
48
+
49
+ # Does the given environment exist and contain the given version?
50
+ #
51
+ # Returns:
52
+ # Boolean indicating whether the given environment exists and contains the
53
+ # given version
54
+ #
55
+ def environment_has_version(environment, version)
56
+ environment = environment.upcase.to_sym
57
+ version = version.to_sym
58
+ !environment_config[environment].nil? and
59
+ !environment_config[environment][version].nil?
60
+ end
61
+
62
+ # Does the given version exist and contain the given service?
63
+ #
64
+ # Returns:
65
+ # Boolean indicating whether the given version exists and contains the
66
+ # given service
67
+ #
68
+ def version_has_service(version, service)
69
+ version = version.to_sym
70
+ service = service.to_sym
71
+ (!service_config[version].nil? and
72
+ service_config[version].include?(service))
73
+ end
74
+
75
+ # Get the default API version.
76
+ #
77
+ # Returns:
78
+ # Default version (as an integer)
79
+ #
80
+ def default_version
81
+ nil
82
+ end
83
+
84
+ # Get the list of service names for a given version
85
+ #
86
+ # Args:
87
+ # - version: the API version (as an integer)
88
+ #
89
+ # Returns:
90
+ # List of names of services (as strings) available for given version
91
+ #
92
+ def services(version)
93
+ service_config[version]
94
+ end
95
+
96
+ # Get the available environments.
97
+ #
98
+ # Returns:
99
+ # List of available environments (as strings)
100
+ #
101
+ def environments
102
+ environment_config.keys
103
+ end
104
+
105
+ # Get the default environment.
106
+ #
107
+ # Returns:
108
+ # Default environment (as a string)
109
+ #
110
+ def default_environment
111
+ nil
112
+ end
113
+
114
+ # Get the endpoint for a service on a given environment and API version.
115
+ #
116
+ # Args:
117
+ # - environment: the service environment to be used (as a string)
118
+ # - version: the API version (as an integer)
119
+ # - service: the name of the API service (as a string)
120
+ #
121
+ # Returns:
122
+ # The endpoint URL (as a string)
123
+ #
124
+ def endpoint(environment, version, service)
125
+ environment = environment.upcase.to_sym
126
+ version = version.to_sym
127
+ service = service.to_sym
128
+ base = get_wsdl_base(environment, version)
129
+ if !subdir_config().nil?
130
+ base = base.to_s + subdir_config()[[version, service]].to_s
131
+ end
132
+ return base.to_s + version.to_s + '/'
133
+ end
134
+
135
+ # Get the subdirectory for a service, for a given API version.
136
+ #
137
+ # Args:
138
+ # - version: the API version (as an integer)
139
+ # - service: the name of the API service (as a string)
140
+ #
141
+ # Returns:
142
+ # The endpoint URL (as a string)
143
+ #
144
+ def subdir(version, service)
145
+ return nil if subdir_config().nil?
146
+ version = version.to_sym
147
+ service = service.to_sym
148
+ subdir_config()[[version, service]]
149
+ end
150
+
151
+ # Get the authentication server details for an environment. Allows to
152
+ # override the auth URL via environmental variable.
153
+ #
154
+ # Args:
155
+ # - environment: the service environment to be used (as a string)
156
+ #
157
+ # Returns:
158
+ # The full URL for the auth server.
159
+ #
160
+ def auth_server(environment)
161
+ auth_server_url = ENV['ADSAPI_AUTH_URL']
162
+ if auth_server_url.nil?
163
+ environment = environment.upcase.to_sym
164
+ auth_server_url = auth_server_config[environment]
165
+ end
166
+ if auth_server_url.nil?
167
+ # If we don't have an entry for this environment, we just return the
168
+ # default server (the same one being used for the default environment)
169
+ auth_server_url = auth_server_config[default_environment()]
170
+ end
171
+ return auth_server_url
172
+ end
173
+
174
+ # Add a new environment to the list.
175
+ #
176
+ # Args:
177
+ # - name: the name for the new environment
178
+ # - endpoint_hash: a hash of base endpoint URLs, indexed by version number,
179
+ # e.g.:
180
+ # { :v13 => 'URL_FOR_v13', :v200906 => 'URL_FOR_v200906' }
181
+ #
182
+ def add_environment(name, endpoint_hash)
183
+ name = name.upcase.to_sym
184
+ environment_config[name] = endpoint_hash
185
+ end
186
+
187
+ # Perform the loading of the necessary source files for a version
188
+ #
189
+ # Args:
190
+ # - version: the API version (as an integer)
191
+ #
192
+ def do_require(version, service)
193
+ version = version.to_sym
194
+ eval("require '#{api_path}/#{version}/#{service}Wrapper.rb'")
195
+ end
196
+
197
+ # Returns the full module name for a given service
198
+ #
199
+ # Args:
200
+ # - version: the API version (as an integer)
201
+ # - service: the service name (as a string)
202
+ #
203
+ # Returns:
204
+ # The full module name for the given service (as a string)
205
+ #
206
+ def module_name(version, service)
207
+ version = version.to_sym
208
+ service = service.to_sym
209
+ return "#{api_name}::#{version.to_s.upcase}::#{service}"
210
+ end
211
+
212
+ # Returns the full interface class name for a given service
213
+ #
214
+ # Args:
215
+ # - version: the API version (as an integer)
216
+ # - service: the service name (as a string)
217
+ #
218
+ # Returns:
219
+ # The full interface class name for the given service (as a string)
220
+ #
221
+ def interface_name(version, service)
222
+ version = version.to_sym
223
+ service = service.to_sym
224
+ return module_name(version, service) + "::#{service}Interface"
225
+ end
226
+
227
+ # Returns the full wrapper class name for a given service
228
+ #
229
+ # Args:
230
+ # - version: the API version (as an integer)
231
+ # - service: the service name (as a string)
232
+ #
233
+ # Returns:
234
+ # The full wrapper class name for the given service (as a string)
235
+ #
236
+ def wrapper_name(version, service)
237
+ version = version.to_sym
238
+ service = service.to_sym
239
+ return module_name(version, service) + "::#{service}Wrapper"
240
+ end
241
+
242
+ # Generates an array of WSDL URLs based on defined Services and version
243
+ # supplied. This method is used by generators to determine what service
244
+ # wrappers to generate.
245
+ #
246
+ # Args:
247
+ # - version: the API version.
248
+ #
249
+ # Returns
250
+ # hash of pairs Service => WSDL URL.
251
+ #
252
+ def get_wsdls(version)
253
+ res = {}
254
+ wsdl_base = get_wsdl_base(default_environment(), version)
255
+ services(version).each do |service|
256
+ path = wsdl_base
257
+ if (!subdir_config().nil?)
258
+ subdir_name = subdir(version, service);
259
+ path = path + subdir_name if !subdir_name.empty?()
260
+ end
261
+ path = path + version.to_s + '/' + service.to_s + '?wsdl'
262
+ res[service.to_s] = path
263
+ end
264
+ return res
265
+ end
266
+
267
+ # Returns WSDL base url defined in Service configuration. Allows to override
268
+ # the base URL via environmental variable.
269
+ #
270
+ # Args:
271
+ # - environment: environment to use like SANDBOX or PRODUCTION
272
+ # - version: the API version.
273
+ #
274
+ # Returns:
275
+ # String containing base URL.
276
+ def get_wsdl_base(environment, version)
277
+ wsdl_base = ENV['ADSAPI_BASE_URL']
278
+ if wsdl_base.nil?
279
+ wsdl_base = environment_config[environment][version]
280
+ end
281
+ return wsdl_base
282
+ end
283
+ end
284
+ end