google-ads-common 0.2.0

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