google-ads-common 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|