google-ads-common 0.5.0 → 0.5.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 +5 -0
- data/lib/ads_common/api.rb +37 -28
- data/lib/ads_common/api_config.rb +1 -1
- data/lib/ads_common/auth/oauth_handler.rb +8 -4
- data/lib/ads_common/build/savon_generator.rb +1 -0
- data/lib/ads_common/build/savon_registry_generator.rb +2 -0
- data/lib/ads_common/config.rb +19 -18
- data/lib/ads_common/errors.rb +3 -2
- data/lib/ads_common/http.rb +15 -10
- data/lib/ads_common/savon_headers/base_header_handler.rb +17 -0
- data/lib/ads_common/savon_service.rb +78 -29
- data/test/test_savon_service.rb +88 -42
- metadata +4 -4
data/ChangeLog
CHANGED
data/lib/ads_common/api.rb
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
# See the License for the specific language governing permissions and
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
|
-
# Generic
|
20
|
+
# Generic API class, to be inherited from and extended by specific APIs.
|
21
21
|
|
22
22
|
require 'logger'
|
23
23
|
|
@@ -41,24 +41,8 @@ module AdsCommon
|
|
41
41
|
|
42
42
|
# Constructor for API.
|
43
43
|
def initialize(provided_config = nil)
|
44
|
-
load_config(provided_config)
|
45
|
-
provided_logger = @config.read('library.logger')
|
46
|
-
self.logger = (provided_logger.nil?) ?
|
47
|
-
create_default_logger() : provided_logger
|
48
|
-
|
49
|
-
# Check for valid environment.
|
50
|
-
env_string = @config.read('service.environment')
|
51
|
-
environment = (env_string.nil?) ? api_config.default_environment :
|
52
|
-
env_string.to_s.upcase.to_sym
|
53
|
-
if api_config.environments.include?(environment)
|
54
|
-
@config.set('service.environment', environment)
|
55
|
-
else
|
56
|
-
raise AdsCommon::Errors::Error,
|
57
|
-
"Unknown or unspecified environment: \"%s\"" % env_string
|
58
|
-
end
|
59
|
-
|
60
|
-
# Service wrappers.
|
61
44
|
@wrappers = {}
|
45
|
+
load_config(provided_config)
|
62
46
|
end
|
63
47
|
|
64
48
|
# Sets the logger to use.
|
@@ -87,14 +71,20 @@ module AdsCommon
|
|
87
71
|
|
88
72
|
# Check if version exists.
|
89
73
|
if !api_config.versions.include?(version)
|
90
|
-
raise AdsCommon::Errors::Error, "Unknown version '%s'
|
74
|
+
raise AdsCommon::Errors::Error, "Unknown version '%s'" % version
|
91
75
|
end
|
92
76
|
|
93
77
|
# Check if the current environment supports the requested version.
|
94
78
|
environment = @config.read('service.environment')
|
79
|
+
|
80
|
+
if !api_config.environments.include?(environment)
|
81
|
+
raise AdsCommon::Errors::Error,
|
82
|
+
"Unknown or unspecified environment: '%s'" % environment
|
83
|
+
end
|
84
|
+
|
95
85
|
if !api_config.environment_has_version(environment, version)
|
96
86
|
raise AdsCommon::Errors::Error,
|
97
|
-
"Environment '%s' does not support version '%s'
|
87
|
+
"Environment '%s' does not support version '%s'" %
|
98
88
|
[environment, version]
|
99
89
|
end
|
100
90
|
|
@@ -168,7 +158,7 @@ module AdsCommon
|
|
168
158
|
# - a list of SOAP header handlers; one per provided header
|
169
159
|
#
|
170
160
|
def soap_header_handlers(auth_handler, header_list, version, wrapper)
|
171
|
-
raise NotImplementedError, 'soap_header_handlers not
|
161
|
+
raise NotImplementedError, 'soap_header_handlers not overridden.'
|
172
162
|
end
|
173
163
|
|
174
164
|
# Auxiliary method to get an authentication handler. Creates a new one if
|
@@ -196,8 +186,7 @@ module AdsCommon
|
|
196
186
|
# - auth handler
|
197
187
|
#
|
198
188
|
def create_auth_handler(environment, version = nil)
|
199
|
-
|
200
|
-
auth_method = auth_method_str.to_s.upcase.to_sym
|
189
|
+
auth_method = @config.read('authentication.method', :CLIENTLOGIN)
|
201
190
|
return case auth_method
|
202
191
|
when :CLIENTLOGIN
|
203
192
|
auth_server = api_config.auth_server(environment)
|
@@ -208,7 +197,7 @@ module AdsCommon
|
|
208
197
|
AdsCommon::Auth::OAuthHandler.new(config, scope)
|
209
198
|
else
|
210
199
|
raise AdsCommon::Errors::Error,
|
211
|
-
"Unknown authentication method '%s'
|
200
|
+
"Unknown authentication method '%s'" % auth_method
|
212
201
|
end
|
213
202
|
end
|
214
203
|
|
@@ -256,13 +245,33 @@ module AdsCommon
|
|
256
245
|
def load_config(provided_config = nil)
|
257
246
|
@config = (provided_config.nil?) ?
|
258
247
|
AdsCommon::Config.new(
|
259
|
-
File.join(ENV['HOME'], default_config_filename)) :
|
248
|
+
File.join(ENV['HOME'], api_config.default_config_filename)) :
|
260
249
|
AdsCommon::Config.new(provided_config)
|
250
|
+
init_config()
|
261
251
|
end
|
262
252
|
|
263
|
-
#
|
264
|
-
def
|
265
|
-
|
253
|
+
# Initializes config with default values and converts existing if required.
|
254
|
+
def init_config()
|
255
|
+
# Set up logger.
|
256
|
+
provided_logger = @config.read('library.logger')
|
257
|
+
self.logger = (provided_logger.nil?) ?
|
258
|
+
create_default_logger() : provided_logger
|
259
|
+
|
260
|
+
# Validating most important parameters.
|
261
|
+
['service.environment', 'authentication.method'].each do |parameter|
|
262
|
+
symbolize_config_value(parameter)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# Converts value of a config key to uppercase symbol.
|
267
|
+
def symbolize_config_value(key)
|
268
|
+
value_str = @config.read(key).to_s
|
269
|
+
if !value_str.nil? and !value_str.empty?
|
270
|
+
value = value_str.upcase.to_sym
|
271
|
+
@config.set(key, value)
|
272
|
+
else
|
273
|
+
@logger.warn("Empty value for required parameter: '%s'" % key)
|
274
|
+
end
|
266
275
|
end
|
267
276
|
|
268
277
|
# Converts log level string (from config) to Logger value.
|
@@ -26,7 +26,7 @@ module AdsCommon
|
|
26
26
|
# Contains helper methods for loading and managing the available services.
|
27
27
|
# This module is meant to be imported into API-specific modules.
|
28
28
|
module ApiConfig
|
29
|
-
ADS_COMMON_VERSION = '0.5.
|
29
|
+
ADS_COMMON_VERSION = '0.5.1'
|
30
30
|
|
31
31
|
# Get the available API versions.
|
32
32
|
#
|
@@ -144,6 +144,8 @@ module AdsCommon
|
|
144
144
|
validate_credentials(credentials)
|
145
145
|
if @consumer.nil?
|
146
146
|
oauth_config = OAUTH_CONFIG.merge({:scope => @scope})
|
147
|
+
proxy = @config.read('connection.proxy')
|
148
|
+
oauth_config[:proxy] = proxy if !proxy.nil?
|
147
149
|
@consumer = OAuth::Consumer.new(credentials[:oauth_consumer_key],
|
148
150
|
credentials[:oauth_consumer_secret], oauth_config)
|
149
151
|
end
|
@@ -198,9 +200,9 @@ module AdsCommon
|
|
198
200
|
callback = credentials[:oauth_callback] || DEFAULT_CALLBACK
|
199
201
|
begin
|
200
202
|
if @request_token.nil?
|
201
|
-
@request_token =
|
202
|
-
|
203
|
-
|
203
|
+
@request_token = credentials[:oauth_request_token] ||
|
204
|
+
@consumer.get_request_token({:oauth_callback => callback},
|
205
|
+
{:scope => @scope})
|
204
206
|
end
|
205
207
|
verification_code = credentials[:oauth_verification_code]
|
206
208
|
if verification_code.nil? || verification_code.empty?
|
@@ -236,7 +238,9 @@ module AdsCommon
|
|
236
238
|
#
|
237
239
|
def raise_oauth_verification_error(request_token, callback)
|
238
240
|
oauth_url = request_token.authorize_url({:oauth_callback => callback})
|
239
|
-
|
241
|
+
error = AdsCommon::Errors::OAuthVerificationRequired.new(
|
242
|
+
oauth_url, request_token)
|
243
|
+
raise error
|
240
244
|
end
|
241
245
|
|
242
246
|
# Extracts key-value pairs from OAuth server response.
|
data/lib/ads_common/config.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
4
4
|
#
|
5
|
-
# Copyright:: Copyright
|
5
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
6
|
#
|
7
7
|
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
8
|
# you may not use this file except in compliance with the License.
|
@@ -54,10 +54,10 @@ module AdsCommon
|
|
54
54
|
if property_path
|
55
55
|
last_node = @config
|
56
56
|
last_name = property_path.split('.').inject(nil) do |last_name, section|
|
57
|
-
last_node = last_node[last_name
|
58
|
-
section
|
57
|
+
last_node = last_node[last_name] ||= {} unless last_name.nil?
|
58
|
+
section.to_sym
|
59
59
|
end
|
60
|
-
last_node[last_name
|
60
|
+
last_node[last_name] = value
|
61
61
|
end
|
62
62
|
return nil
|
63
63
|
end
|
@@ -65,6 +65,21 @@ module AdsCommon
|
|
65
65
|
# Writes an entire set of properties.
|
66
66
|
def set_all(properties)
|
67
67
|
@config = process_hash_keys(properties)
|
68
|
+
return nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Reads a configuration file into instance variable as a Ruby structure with
|
72
|
+
# the complete set of keys and values.
|
73
|
+
#
|
74
|
+
# Args:
|
75
|
+
# - filename: config file to be read (*String*)
|
76
|
+
#
|
77
|
+
# Raises:
|
78
|
+
# - <b>Errno::ENOENT</b> if the file does not exist.
|
79
|
+
#
|
80
|
+
def load(filename)
|
81
|
+
@config = YAML::load_file(filename)
|
82
|
+
return nil
|
68
83
|
end
|
69
84
|
|
70
85
|
private
|
@@ -89,19 +104,5 @@ module AdsCommon
|
|
89
104
|
(node.is_a?(Hash) and node.include?(key)) ? node[key] : nil
|
90
105
|
end
|
91
106
|
end
|
92
|
-
|
93
|
-
# Reads a configuration file into instance variable as a Ruby structure with
|
94
|
-
# the complete set of keys and values.
|
95
|
-
#
|
96
|
-
# Args:
|
97
|
-
# - filename: config file to be read (*String*)
|
98
|
-
#
|
99
|
-
# Raises:
|
100
|
-
# - <b>Errno::ENOENT</b> if the file does not exist.
|
101
|
-
#
|
102
|
-
def load(filename)
|
103
|
-
@config = YAML::load_file(filename)
|
104
|
-
return nil
|
105
|
-
end
|
106
107
|
end
|
107
108
|
end
|
data/lib/ads_common/errors.rb
CHANGED
@@ -42,9 +42,10 @@ module AdsCommon
|
|
42
42
|
# Raised when OAuth access token is required.
|
43
43
|
class OAuthVerificationRequired < AuthError
|
44
44
|
attr_reader :oauth_url
|
45
|
-
|
45
|
+
attr_reader :request_token
|
46
|
+
def initialize(oauth_url, request_token)
|
46
47
|
super()
|
47
|
-
@oauth_url = oauth_url
|
48
|
+
@oauth_url, @request_token = oauth_url, request_token
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
data/lib/ads_common/http.rb
CHANGED
@@ -30,7 +30,7 @@ module AdsCommon
|
|
30
30
|
# Performs a get on a URL, using all of the connection options in the
|
31
31
|
# client library, returning a HTTPI::Response.
|
32
32
|
def self.get_response(url, config = nil, headers = nil)
|
33
|
-
request = prepare_request(
|
33
|
+
request = prepare_request(url, config, headers)
|
34
34
|
response = HTTPI.get(request)
|
35
35
|
return response
|
36
36
|
end
|
@@ -44,7 +44,7 @@ module AdsCommon
|
|
44
44
|
# Performs a post on a URL, using all of the connection options in the
|
45
45
|
# client library, returning a HTTPI::Response.
|
46
46
|
def self.post_response(url, data, config = nil, headers = nil)
|
47
|
-
request = prepare_request(
|
47
|
+
request = prepare_request(url, config, headers, data)
|
48
48
|
response = HTTPI.post(request)
|
49
49
|
return response
|
50
50
|
end
|
@@ -59,17 +59,22 @@ module AdsCommon
|
|
59
59
|
|
60
60
|
# Returns a suitably configured request object for a given URL and options.
|
61
61
|
# Defaulting to stricter :peer validation.
|
62
|
-
def self.prepare_request(
|
62
|
+
def self.prepare_request(url, config = nil, headers = nil, data = nil)
|
63
63
|
request = HTTPI::Request.new(url)
|
64
|
-
proxy = config.read('connection.proxy', nil)
|
65
|
-
request.proxy = proxy if proxy
|
66
|
-
strict_ssl = config.nil? or
|
67
|
-
!(config.read('connection.strict_ssl_verification') == 'false')
|
68
|
-
request.auth.ssl.verify_mode = strict_ssl ? :peer : :none
|
69
64
|
request.headers = headers if headers
|
70
65
|
request.body = data if data
|
71
|
-
|
72
|
-
|
66
|
+
if config
|
67
|
+
proxy = config.read('connection.proxy', nil)
|
68
|
+
request.proxy = proxy if !proxy.nil?
|
69
|
+
strict_ssl =
|
70
|
+
!(config.read('connection.strict_ssl_verification') == 'false')
|
71
|
+
request.auth.ssl.verify_mode = strict_ssl ? :peer : :none
|
72
|
+
logger = config.read('library.logger')
|
73
|
+
if logger
|
74
|
+
HTTPI.logger = logger
|
75
|
+
HTTPI.log_level = :debug
|
76
|
+
end
|
77
|
+
end
|
73
78
|
return request
|
74
79
|
end
|
75
80
|
end
|
@@ -19,6 +19,8 @@
|
|
19
19
|
#
|
20
20
|
# Base class for handlers of SOAP headers.
|
21
21
|
|
22
|
+
require 'savon'
|
23
|
+
|
22
24
|
module AdsCommon
|
23
25
|
module SavonHeaders
|
24
26
|
class BaseHeaderHandler
|
@@ -62,8 +64,12 @@ module AdsCommon
|
|
62
64
|
soap.body = args if args
|
63
65
|
# Sets the default namespace for the body.
|
64
66
|
soap.input[2] = {:xmlns => @namespace}
|
67
|
+
# Sets User-Agent in the HTTP header.
|
68
|
+
request.headers['User-Agent'] = generate_user_agent_string()
|
65
69
|
end
|
66
70
|
|
71
|
+
private
|
72
|
+
|
67
73
|
# Adds namespace to the given string.
|
68
74
|
#
|
69
75
|
# Args:
|
@@ -75,6 +81,17 @@ module AdsCommon
|
|
75
81
|
def prepend_namespace(str)
|
76
82
|
return "%s:%s" % [DEFAULT_NAMESPACE, str]
|
77
83
|
end
|
84
|
+
|
85
|
+
# Generates User-Agent text for HTTP request.
|
86
|
+
def generate_user_agent_string()
|
87
|
+
credentials = @credential_handler.credentials
|
88
|
+
app_name = credentials[:user_agent]
|
89
|
+
# We don't know the library version here. A breaking change needs to be
|
90
|
+
# introduced. This is scheduled for 0.6.0, using Common version for now.
|
91
|
+
lib_version = '0.5.1'
|
92
|
+
soap_user_agent = "Common-Ruby-%s; %s" % [lib_version, app_name]
|
93
|
+
return "Savon/%s (%s)" % [Savon::Version, soap_user_agent]
|
94
|
+
end
|
78
95
|
end
|
79
96
|
end
|
80
97
|
end
|
@@ -19,6 +19,7 @@
|
|
19
19
|
#
|
20
20
|
# Base class for all generated API services based on Savon backend.
|
21
21
|
|
22
|
+
require 'httpi'
|
22
23
|
require 'savon'
|
23
24
|
|
24
25
|
module AdsCommon
|
@@ -26,6 +27,9 @@ module AdsCommon
|
|
26
27
|
# Default namespace name.
|
27
28
|
DEFAULT_NAMESPACE = 'wsdl'
|
28
29
|
|
30
|
+
# HTTP read timeout in seconds.
|
31
|
+
HTTP_READ_TIMEOUT = 15 * 60
|
32
|
+
|
29
33
|
attr_accessor :headerhandler
|
30
34
|
attr_reader :api
|
31
35
|
attr_reader :version
|
@@ -34,28 +38,45 @@ module AdsCommon
|
|
34
38
|
# Creates a new service.
|
35
39
|
def initialize(api, endpoint, namespace, version)
|
36
40
|
if self.class() == AdsCommon::SavonService
|
37
|
-
raise NoMethodError,
|
41
|
+
raise NoMethodError, 'Tried to instantiate an abstract class'
|
38
42
|
end
|
39
|
-
@api = api
|
40
|
-
@version = version
|
41
|
-
@namespace = namespace
|
43
|
+
@api, @version, @namespace = api, version, namespace
|
42
44
|
@headerhandler = []
|
43
|
-
@client =
|
44
|
-
wsdl.namespace = namespace
|
45
|
-
wsdl.endpoint = endpoint
|
46
|
-
end
|
45
|
+
@client = create_savon_client(endpoint, namespace)
|
47
46
|
end
|
48
47
|
|
49
48
|
private
|
50
49
|
|
51
|
-
#
|
50
|
+
# Sets the logger in Savon-specific way. Also sets it for HTTPI used by it.
|
51
|
+
def self.logger=(logger)
|
52
|
+
Savon.configure do |config|
|
53
|
+
config.log_level = :debug
|
54
|
+
config.logger = logger
|
55
|
+
end
|
56
|
+
HTTPI.logger = logger
|
57
|
+
HTTPI.log_level = :debug
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns ServiceRegistry for the current service. Has to be overridden.
|
52
61
|
def get_service_registry()
|
53
|
-
raise NoMethodError,
|
62
|
+
raise NoMethodError, 'This method needs to be overridden'
|
54
63
|
end
|
55
64
|
|
56
|
-
# Returns Module for the current service. Has to be
|
65
|
+
# Returns Module for the current service. Has to be overridden.
|
57
66
|
def get_module()
|
58
|
-
raise NoMethodError,
|
67
|
+
raise NoMethodError, 'This method needs to be overridden'
|
68
|
+
end
|
69
|
+
|
70
|
+
# Creates and sets up Savon client.
|
71
|
+
def create_savon_client(endpoint, namespace)
|
72
|
+
proxy = @api.config.read('connection.proxy')
|
73
|
+
client = Savon::Client.new do |wsdl, http|
|
74
|
+
wsdl.endpoint = endpoint
|
75
|
+
wsdl.namespace = namespace
|
76
|
+
http.proxy = proxy if !proxy.nil?
|
77
|
+
http.read_timeout = HTTP_READ_TIMEOUT
|
78
|
+
end
|
79
|
+
return client
|
59
80
|
end
|
60
81
|
|
61
82
|
# Executes SOAP action specified as a string with given arguments.
|
@@ -131,7 +152,7 @@ module AdsCommon
|
|
131
152
|
xsi_field_type = get_full_type_signature(xsi_type)
|
132
153
|
if xsi_field_type.nil?
|
133
154
|
raise AdsCommon::Errors::ApiException.new(
|
134
|
-
"Incorrect xsi:type specified: %s" % [xsi_type])
|
155
|
+
"Incorrect xsi:type specified: '%s'" % [xsi_type])
|
135
156
|
else
|
136
157
|
# TODO: make sure xsi_type is derived from field_type.
|
137
158
|
field_type = xsi_field_type
|
@@ -167,7 +188,7 @@ module AdsCommon
|
|
167
188
|
[subtype_name, subtype]
|
168
189
|
end
|
169
190
|
# In case of non-default namespace, the children should be in
|
170
|
-
#
|
191
|
+
# overridden namespace but the node has to be in the default.
|
171
192
|
# We also have to fix order! list if we alter the key name.
|
172
193
|
new_key = if (subtype and subtype[:ns])
|
173
194
|
prefixed_key = prefix_key(k)
|
@@ -196,7 +217,12 @@ module AdsCommon
|
|
196
217
|
def add_attribute(node, key, name, value)
|
197
218
|
node[:attributes!] ||= {}
|
198
219
|
node[:attributes!][key] ||= {}
|
199
|
-
node[:attributes!][key]
|
220
|
+
if node[:attributes!][key].include?(name)
|
221
|
+
node[:attributes!][key][name] = arrayize(node[:attributes!][key][name])
|
222
|
+
node[:attributes!][key][name] << value
|
223
|
+
else
|
224
|
+
node[:attributes!][key][name] = value
|
225
|
+
end
|
200
226
|
end
|
201
227
|
|
202
228
|
# Prefixes default namespace.
|
@@ -253,7 +279,24 @@ module AdsCommon
|
|
253
279
|
action = method[:output][:name].to_sym
|
254
280
|
result = response.to_hash
|
255
281
|
result = result[action] if result.include?(action)
|
256
|
-
|
282
|
+
result = normalize_output(result, method)
|
283
|
+
result[:header] = extract_header_data(response) if result.kind_of?(Hash)
|
284
|
+
return result
|
285
|
+
end
|
286
|
+
|
287
|
+
# Extracts misc data from response header.
|
288
|
+
def extract_header_data(response)
|
289
|
+
header_type = get_full_type_signature(:SoapResponseHeader)
|
290
|
+
headers = response.header[:response_header].dup
|
291
|
+
result = headers.inject({}) do |result, (key, v)|
|
292
|
+
# Attributes start with '@' and are not included in type definition.
|
293
|
+
if !(key.to_s.start_with?('@'))
|
294
|
+
normalize_output_field(headers, header_type[:fields], key)
|
295
|
+
result[key] = headers[key]
|
296
|
+
end
|
297
|
+
result
|
298
|
+
end
|
299
|
+
return result
|
257
300
|
end
|
258
301
|
|
259
302
|
# Normalizes output starting with root node "rval".
|
@@ -271,26 +314,28 @@ module AdsCommon
|
|
271
314
|
def normalize_output_field(output_data, fields_list, field_name)
|
272
315
|
return nil if output_data.nil?
|
273
316
|
field_definition = get_field_by_name(fields_list, field_name)
|
317
|
+
if field_definition.nil?
|
318
|
+
@api.logger.warn("Can not determine type for field: %s" % field_name)
|
319
|
+
return output_data
|
320
|
+
end
|
321
|
+
|
274
322
|
field_sym = field_name.to_sym
|
275
|
-
|
276
|
-
|
323
|
+
field_data = normalize_type(output_data[field_sym], field_definition)
|
324
|
+
output_data[field_sym] = field_data if field_data
|
277
325
|
|
278
326
|
sub_type = get_full_type_signature(field_definition[:type])
|
279
327
|
if sub_type and sub_type[:fields]
|
280
328
|
# go recursive
|
281
329
|
sub_type[:fields].each do |sub_type_field|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
normalize_output_field(item, sub_type_field,
|
288
|
-
sub_type_field[:name])
|
330
|
+
field_data = output_data[field_sym]
|
331
|
+
if field_data.is_a?(Array)
|
332
|
+
field_data.each do |item|
|
333
|
+
normalize_output_field(item, sub_type_field,
|
334
|
+
sub_type_field[:name])
|
289
335
|
end
|
290
336
|
else
|
291
|
-
|
292
|
-
|
293
|
-
sub_type_field[:name])
|
337
|
+
normalize_output_field(field_data, sub_type_field,
|
338
|
+
sub_type_field[:name])
|
294
339
|
end
|
295
340
|
end
|
296
341
|
end
|
@@ -356,7 +401,11 @@ module AdsCommon
|
|
356
401
|
parent_type = get_service_registry.get_type_signature(data_type[:base])
|
357
402
|
result += implode_parent(parent_type)
|
358
403
|
end
|
359
|
-
|
404
|
+
data_type[:fields].each do |field|
|
405
|
+
# If the parent type includes a field with the same name, overwrite it.
|
406
|
+
result.reject! {|parent_field| parent_field[:name].eql?(field[:name])}
|
407
|
+
result << field
|
408
|
+
end
|
360
409
|
return result
|
361
410
|
end
|
362
411
|
|
data/test/test_savon_service.rb
CHANGED
@@ -22,12 +22,25 @@
|
|
22
22
|
require 'rubygems'
|
23
23
|
require 'test/unit'
|
24
24
|
|
25
|
+
require 'ads_common/config'
|
25
26
|
require 'ads_common/savon_service'
|
26
27
|
|
28
|
+
# AdsCommon::Api is abstract, defining a stub class for the test.
|
29
|
+
class StubApi
|
30
|
+
attr_accessor :config
|
31
|
+
def initialize()
|
32
|
+
@config = AdsCommon::Config.new
|
33
|
+
end
|
34
|
+
def self.get_instance()
|
35
|
+
@api ||= StubApi.new
|
36
|
+
return @api
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
27
40
|
# SavonService is abstract, defining a child class for the test.
|
28
|
-
class
|
41
|
+
class StubService < AdsCommon::SavonService
|
29
42
|
def initialize(namespace, endpoint, version)
|
30
|
-
super(
|
43
|
+
super(StubApi.get_instance(), namespace, endpoint, version)
|
31
44
|
end
|
32
45
|
def private_get_service_registry()
|
33
46
|
return get_service_registry
|
@@ -47,6 +60,9 @@ class SomeService < AdsCommon::SavonService
|
|
47
60
|
def private_deep_copy(object)
|
48
61
|
return deep_copy(object)
|
49
62
|
end
|
63
|
+
def private_add_attribute(node, key, name, value)
|
64
|
+
return add_attribute(node, key, name, value)
|
65
|
+
end
|
50
66
|
end
|
51
67
|
|
52
68
|
class TestSavonService < Test::Unit::TestCase
|
@@ -56,7 +72,7 @@ class TestSavonService < Test::Unit::TestCase
|
|
56
72
|
|
57
73
|
# Initialize tests.
|
58
74
|
def setup
|
59
|
-
@
|
75
|
+
@stub_service = StubService.new(TEST_NAMESPACE, TEST_ENDPOINT, TEST_VERSION)
|
60
76
|
end
|
61
77
|
|
62
78
|
def test_initialize_abstract
|
@@ -65,35 +81,35 @@ class TestSavonService < Test::Unit::TestCase
|
|
65
81
|
TEST_VERSION)
|
66
82
|
end
|
67
83
|
assert_nothing_raised do
|
68
|
-
|
84
|
+
StubService.new(TEST_NAMESPACE, TEST_ENDPOINT, TEST_VERSION)
|
69
85
|
end
|
70
86
|
end
|
71
87
|
|
72
88
|
def test_get_service_registry_abstract
|
73
|
-
assert_raises(NoMethodError) { @
|
89
|
+
assert_raises(NoMethodError) { @stub_service.private_get_service_registry }
|
74
90
|
end
|
75
91
|
|
76
92
|
def test_get_module_abstract
|
77
|
-
assert_raises(NoMethodError) { @
|
93
|
+
assert_raises(NoMethodError) { @stub_service.private_get_module }
|
78
94
|
end
|
79
95
|
|
80
96
|
def test_arrayize_empty
|
81
|
-
result1 = @
|
97
|
+
result1 = @stub_service.private_arrayize(nil)
|
82
98
|
assert_instance_of(Array, result1, 'returned object is not an Array')
|
83
99
|
assert_equal(0, result1.size, 'array is not empty')
|
84
100
|
|
85
|
-
result2 = @
|
101
|
+
result2 = @stub_service.private_arrayize([])
|
86
102
|
assert_instance_of(Array, result2, 'returned object is not an Array')
|
87
103
|
assert_equal(0, result2.size, 'array is not empty')
|
88
104
|
end
|
89
105
|
|
90
106
|
def test_arrayize_on_array
|
91
|
-
result1 = @
|
107
|
+
result1 = @stub_service.private_arrayize([nil])
|
92
108
|
assert_instance_of(Array, result1, 'returned object is not an Array')
|
93
109
|
assert_equal(1, result1.size, 'array changed size')
|
94
110
|
assert_equal(nil, result1[0], 'array changed data')
|
95
111
|
|
96
|
-
result2 = @
|
112
|
+
result2 = @stub_service.private_arrayize(['a', 'b'])
|
97
113
|
assert_instance_of(Array, result2, 'returned object is not an Array')
|
98
114
|
assert_equal(2, result2.size, 'array changed size')
|
99
115
|
assert_equal('a', result2[0], 'array changed data')
|
@@ -101,119 +117,124 @@ class TestSavonService < Test::Unit::TestCase
|
|
101
117
|
end
|
102
118
|
|
103
119
|
def test_normalize_type_int
|
104
|
-
result1 = @
|
120
|
+
result1 = @stub_service.private_normalize_type(5, {:type => 'int'})
|
105
121
|
assert_kind_of(Integer, result1)
|
106
122
|
assert_equal(5, result1, 'bad conversion')
|
107
123
|
|
108
|
-
result2 = @
|
124
|
+
result2 = @stub_service.private_normalize_type(2147483648, {:type => 'int'})
|
109
125
|
assert_kind_of(Integer, result2)
|
110
126
|
assert_equal(2147483648, result2, 'bad conversion')
|
111
127
|
end
|
112
128
|
|
113
129
|
def test_normalize_type_string
|
114
|
-
result1 = @
|
130
|
+
result1 = @stub_service.private_normalize_type('foobar',
|
115
131
|
{:type => 'string'})
|
116
132
|
assert_kind_of(String, result1)
|
117
133
|
assert_equal('foobar', result1, 'bad conversion')
|
118
134
|
|
119
|
-
result2 = @
|
135
|
+
result2 = @stub_service.private_normalize_type('', {:type => 'string'})
|
120
136
|
assert_kind_of(String, result2)
|
121
137
|
assert_equal('', result2, 'bad conversion')
|
122
138
|
end
|
123
139
|
|
124
140
|
def test_normalize_type_long
|
125
|
-
result1 = @
|
141
|
+
result1 = @stub_service.private_normalize_type(2147483648,
|
126
142
|
{:type => 'long'})
|
127
143
|
assert_kind_of(Integer, result1)
|
128
144
|
assert_equal(2147483648, result1, 'bad conversion')
|
129
145
|
|
130
|
-
result2 = @
|
146
|
+
result2 = @stub_service.private_normalize_type(-1, {:type => 'long'})
|
131
147
|
assert_kind_of(Integer, result2)
|
132
148
|
assert_equal(-1, result2, 'bad conversion')
|
133
149
|
end
|
134
150
|
|
135
151
|
def test_normalize_type_boolean
|
136
|
-
result1 = @
|
152
|
+
result1 = @stub_service.private_normalize_type(true, {:type => 'boolean'})
|
137
153
|
assert_kind_of(TrueClass, result1)
|
138
154
|
|
139
|
-
result2 = @
|
155
|
+
result2 = @stub_service.private_normalize_type(false, {:type => 'boolean'})
|
140
156
|
assert_kind_of(FalseClass, result2)
|
141
157
|
|
142
|
-
result3 = @
|
158
|
+
result3 = @stub_service.private_normalize_type('true', {:type => 'boolean'})
|
143
159
|
assert_kind_of(TrueClass, result3)
|
144
160
|
|
145
|
-
result4 = @
|
161
|
+
result4 = @stub_service.private_normalize_type('false',
|
146
162
|
{:type => 'boolean'})
|
147
163
|
assert_kind_of(FalseClass, result4)
|
148
164
|
|
149
|
-
result5 = @
|
165
|
+
result5 = @stub_service.private_normalize_type('True',
|
150
166
|
{:type => 'boolean'})
|
151
167
|
assert_kind_of(TrueClass, result3)
|
152
168
|
|
153
|
-
result6 = @
|
169
|
+
result6 = @stub_service.private_normalize_type('False',
|
154
170
|
{:type => 'boolean'})
|
155
171
|
assert_kind_of(FalseClass, result4)
|
156
172
|
end
|
157
173
|
|
158
174
|
def test_normalize_type_object
|
159
|
-
result1 = @
|
160
|
-
{:type => '
|
175
|
+
result1 = @stub_service.private_normalize_type({:a => 'b'},
|
176
|
+
{:type => 'StubClass'})
|
161
177
|
assert_equal('b', result1[:a], 'object corrupted')
|
162
178
|
|
163
|
-
result2 = @
|
179
|
+
result2 = @stub_service.private_normalize_type(@stub_service,
|
164
180
|
{:type => 'SavonService'})
|
165
|
-
assert_equal(@
|
181
|
+
assert_equal(@stub_service.hash, result2.hash, 'object corrupted')
|
166
182
|
end
|
167
183
|
|
168
184
|
def test_normalize_type_double
|
169
|
-
result1 = @
|
185
|
+
result1 = @stub_service.send(:private_normalize_type, 3.14,
|
170
186
|
{:type => 'double'})
|
171
187
|
assert_kind_of(Float, result1)
|
172
188
|
assert_equal(3.14, result1, 'bad conversion')
|
173
189
|
|
174
|
-
result2 = @
|
190
|
+
result2 = @stub_service.send(:private_normalize_type, '-3.14',
|
175
191
|
{:type => 'double'})
|
176
192
|
assert_kind_of(Float, result2)
|
177
193
|
assert_equal(-3.14, result2, 'bad conversion')
|
178
194
|
|
179
|
-
result3 = @
|
195
|
+
result3 = @stub_service.send(:private_normalize_type, '42',
|
180
196
|
{:type => 'double'})
|
181
197
|
assert_kind_of(Float, result3)
|
182
198
|
assert_equal(42.0, result3, 'bad conversion')
|
183
199
|
end
|
184
200
|
|
185
201
|
def test_normalize_type_single_array_item
|
186
|
-
result1 = @
|
202
|
+
result1 = @stub_service.private_normalize_type('42',
|
187
203
|
{:type => 'double', :min_occurs => '0', :max_occurs => 1})
|
188
204
|
assert_kind_of(Float, result1)
|
189
|
-
assert_equal(42.0, result1, 'Float expected for max_occurs 1')
|
205
|
+
assert_equal(42.0, result1, 'Float is expected for max_occurs 1')
|
190
206
|
|
191
|
-
result2 = @
|
192
|
-
{:type => 'double', :min_occurs => '0', :max_occurs =>
|
207
|
+
result2 = @stub_service.private_normalize_type('42',
|
208
|
+
{:type => 'double', :min_occurs => '0', :max_occurs => :unbounded})
|
193
209
|
assert_instance_of(Array, result2)
|
194
|
-
assert_equal(42.0, result2[0], 'Array is expected for
|
210
|
+
assert_equal(42.0, result2[0], 'Array is expected for unbounded max_occurs')
|
195
211
|
|
196
|
-
result3 = @
|
212
|
+
result3 = @stub_service.private_normalize_type('42',
|
197
213
|
{:type => 'double', :min_occurs => '0', :max_occurs => 2})
|
198
214
|
assert_instance_of(Array, result3)
|
199
215
|
assert_equal(42.0, result3[0], 'Array is expected for max_occurs > 1')
|
216
|
+
|
217
|
+
result4 = @stub_service.private_normalize_type('42',
|
218
|
+
{:type => 'double', :min_occurs => '0', :max_occurs => nil})
|
219
|
+
assert_instance_of(Float, result4)
|
220
|
+
assert_equal(42.0, result4, 'Float is expected for nil max_occurs')
|
200
221
|
end
|
201
222
|
|
202
223
|
def test_deep_copy_simple
|
203
|
-
result1 = @
|
224
|
+
result1 = @stub_service.private_deep_copy(42)
|
204
225
|
assert_equal(42, result1)
|
205
226
|
|
206
|
-
result2 = @
|
227
|
+
result2 = @stub_service.private_deep_copy('Hello World')
|
207
228
|
assert_equal('Hello World', result2)
|
208
229
|
|
209
|
-
result3 = @
|
230
|
+
result3 = @stub_service.private_deep_copy(nil)
|
210
231
|
assert_nil(result3)
|
211
232
|
|
212
|
-
result4 = @
|
233
|
+
result4 = @stub_service.private_deep_copy([])
|
213
234
|
assert_equal([], result4)
|
214
235
|
assert_not_same([], result4)
|
215
236
|
|
216
|
-
result5 = @
|
237
|
+
result5 = @stub_service.private_deep_copy({})
|
217
238
|
assert_equal({}, result5)
|
218
239
|
assert_not_same({}, result5)
|
219
240
|
end
|
@@ -221,11 +242,11 @@ class TestSavonService < Test::Unit::TestCase
|
|
221
242
|
def test_deep_copy_complex
|
222
243
|
data = {:ab => 'ab', :cd => ['cd', 'de', 'ef']}
|
223
244
|
|
224
|
-
result1 = @
|
245
|
+
result1 = @stub_service.private_deep_copy(data)
|
225
246
|
assert_equal(data, result1)
|
226
247
|
assert_not_same(data, result1)
|
227
248
|
|
228
|
-
result2 = @
|
249
|
+
result2 = @stub_service.private_deep_copy(data)
|
229
250
|
assert_equal(result2, result1)
|
230
251
|
assert_not_same(result2, result1)
|
231
252
|
|
@@ -233,4 +254,29 @@ class TestSavonService < Test::Unit::TestCase
|
|
233
254
|
assert_not_equal(data, result2)
|
234
255
|
assert_equal(data, result1)
|
235
256
|
end
|
257
|
+
|
258
|
+
def test_add_attribute
|
259
|
+
node = {}
|
260
|
+
|
261
|
+
key, name, value1, value2, value3 = 'key', 'name', 'Lorem', 'ipsum', 'dolor'
|
262
|
+
|
263
|
+
@stub_service.private_add_attribute(node, key, name, value1)
|
264
|
+
assert_kind_of(Hash, node)
|
265
|
+
assert_kind_of(Hash, node[:attributes!])
|
266
|
+
assert_kind_of(Hash, node[:attributes!][key])
|
267
|
+
assert_equal(value1, node[:attributes!][key][name])
|
268
|
+
|
269
|
+
@stub_service.private_add_attribute(node, key, name, value2)
|
270
|
+
assert_kind_of(Hash, node)
|
271
|
+
assert_kind_of(Hash, node[:attributes!])
|
272
|
+
assert_kind_of(Hash, node[:attributes!][key])
|
273
|
+
assert_kind_of(Array, node[:attributes!][key][name])
|
274
|
+
assert_equal(value1, node[:attributes!][key][name][0])
|
275
|
+
assert_equal(value2, node[:attributes!][key][name][1])
|
276
|
+
|
277
|
+
@stub_service.private_add_attribute(node, key, name, value3)
|
278
|
+
assert_equal(value1, node[:attributes!][key][name][0])
|
279
|
+
assert_equal(value2, node[:attributes!][key][name][1])
|
280
|
+
assert_equal(value3, node[:attributes!][key][name][2])
|
281
|
+
end
|
236
282
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google-ads-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 1
|
10
|
+
version: 0.5.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sergio Gomes
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-
|
19
|
+
date: 2011-09-21 00:00:00 +04:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|