google-ads-common 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +5 -0
- data/README +1 -4
- data/Rakefile +2 -2
- data/lib/ads_common/api.rb +106 -16
- data/lib/ads_common/api_config.rb +2 -3
- data/lib/ads_common/auth/base_handler.rb +22 -3
- data/lib/ads_common/auth/client_login_handler.rb +27 -32
- data/lib/ads_common/auth/oauth_handler.rb +260 -0
- data/lib/ads_common/build/savon_abstract_generator.rb +12 -11
- data/lib/ads_common/build/savon_generator.rb +31 -27
- data/lib/ads_common/build/savon_registry.rb +46 -23
- data/lib/ads_common/build/savon_registry_generator.rb +23 -10
- data/lib/ads_common/build/savon_service_generator.rb +17 -3
- data/lib/ads_common/config.rb +1 -1
- data/lib/ads_common/credential_handler.rb +3 -7
- data/lib/ads_common/errors.rb +18 -6
- data/lib/ads_common/savon_headers/base_header_handler.rb +80 -0
- data/lib/ads_common/{soap4r_logger.rb → savon_headers/httpi_request_proxy.rb} +27 -20
- data/lib/ads_common/savon_headers/oauth_header_handler.rb +92 -0
- data/lib/ads_common/savon_headers/simple_header_handler.rb +17 -49
- data/lib/ads_common/savon_service.rb +129 -41
- data/test/test_savon_service.rb +9 -4
- metadata +39 -43
- data/lib/ads_common/build/rake_common.rb +0 -343
- data/lib/ads_common/build/soap4r_generator.rb +0 -565
- data/lib/ads_common/savon_headers/client_login_header_handler.rb +0 -60
- data/lib/ads_common/soap4r_headers/nested_header_handler.rb +0 -50
- data/lib/ads_common/soap4r_headers/single_header_handler.rb +0 -44
- data/lib/ads_common/soap4r_patches.rb +0 -210
- data/lib/ads_common/soap4r_response_handler.rb +0 -80
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Authors:: api.sgomes@gmail.com (Sérgio Gomes)
|
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.
|
@@ -21,17 +21,13 @@
|
|
21
21
|
|
22
22
|
module AdsCommon
|
23
23
|
class CredentialHandler
|
24
|
+
attr_reader :credentials
|
24
25
|
|
25
26
|
def initialize(config)
|
26
27
|
@config = config
|
27
28
|
load_from_config
|
28
29
|
end
|
29
30
|
|
30
|
-
# Retrieve all credentials (optional parameter specifying API version).
|
31
|
-
def credentials(version = nil)
|
32
|
-
return @credentials
|
33
|
-
end
|
34
|
-
|
35
31
|
# Set the credentials hash to a new one. Calculate difference, and call the
|
36
32
|
# AuthHandler callback appropriately.
|
37
33
|
def credentials=(new_credentials)
|
@@ -59,7 +55,7 @@ module AdsCommon
|
|
59
55
|
# appropriately.
|
60
56
|
def set_credential(credential, value)
|
61
57
|
@credentials[credential] = value
|
62
|
-
@auth_handler.property_changed(credential, value) if auth_handler
|
58
|
+
@auth_handler.property_changed(credential, value) if @auth_handler
|
63
59
|
end
|
64
60
|
|
65
61
|
private
|
data/lib/ads_common/errors.rb
CHANGED
@@ -27,9 +27,25 @@ module AdsCommon
|
|
27
27
|
class Error < ::StandardError
|
28
28
|
end
|
29
29
|
|
30
|
-
# Raised if an attempt is made to authenticate
|
31
|
-
#
|
30
|
+
# Raised if an attempt is made to authenticate with missing or wrong
|
31
|
+
# information.
|
32
32
|
class AuthError < Error
|
33
|
+
attr_reader :error
|
34
|
+
attr_reader :info
|
35
|
+
def initialize(message = self.class.to_s, error = nil, info = nil)
|
36
|
+
super(message)
|
37
|
+
@error = error
|
38
|
+
@info = info
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Raised when OAuth access token is required.
|
43
|
+
class OAuthVerificationRequired < AuthError
|
44
|
+
attr_reader :oauth_url
|
45
|
+
def initialize(oauth_url)
|
46
|
+
super()
|
47
|
+
@oauth_url = oauth_url
|
48
|
+
end
|
33
49
|
end
|
34
50
|
|
35
51
|
# Raised if setting a non-existant property on an object
|
@@ -57,14 +73,10 @@ module AdsCommon
|
|
57
73
|
# Superclass for API exceptions. Each client library should implement its
|
58
74
|
# own subclass with extra fields.
|
59
75
|
class ApiException < Error
|
60
|
-
|
61
76
|
attr_reader :message
|
62
|
-
|
63
77
|
def initialize(message = nil)
|
64
78
|
@message = message
|
65
79
|
end
|
66
|
-
|
67
80
|
end
|
68
|
-
|
69
81
|
end
|
70
82
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
4
|
+
#
|
5
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
16
|
+
# implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
# Base class for handlers of SOAP headers.
|
21
|
+
|
22
|
+
module AdsCommon
|
23
|
+
module SavonHeaders
|
24
|
+
class BaseHeaderHandler
|
25
|
+
# Default namespace alias.
|
26
|
+
DEFAULT_NAMESPACE = 'wsdl'
|
27
|
+
|
28
|
+
# Initializes a header handler.
|
29
|
+
#
|
30
|
+
# Args:
|
31
|
+
# - credential_handler: a header with credential data
|
32
|
+
# - auth_handler: a header with auth data
|
33
|
+
# - element_name: an API-specific name of header element
|
34
|
+
# - namespace: default namespace to use
|
35
|
+
# - version: services version
|
36
|
+
#
|
37
|
+
def initialize(credential_handler, auth_handler, element_name,
|
38
|
+
namespace, version)
|
39
|
+
@credential_handler = credential_handler
|
40
|
+
@auth_handler = auth_handler
|
41
|
+
@element_name = element_name
|
42
|
+
@namespace = namespace
|
43
|
+
@version = version
|
44
|
+
Savon.configure {|config| config.raise_errors = false}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Enriches soap object with API-specific headers like namespaces, login
|
48
|
+
# credentials etc.
|
49
|
+
#
|
50
|
+
# Needs to be overriden.
|
51
|
+
#
|
52
|
+
# Args:
|
53
|
+
# - request: a HTTPI Request for extra configuration
|
54
|
+
# - soap: a Savon soap object to fill fields in
|
55
|
+
# - args: request parameters to adjust for namespaces
|
56
|
+
#
|
57
|
+
# Returns:
|
58
|
+
# - Modified request and soap structures
|
59
|
+
#
|
60
|
+
def prepare_request(request, soap, args)
|
61
|
+
soap.namespace = @namespace
|
62
|
+
soap.body = args if args
|
63
|
+
# Sets the default namespace for the body.
|
64
|
+
soap.input[2] = {:xmlns => @namespace}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Adds namespace to the given string.
|
68
|
+
#
|
69
|
+
# Args:
|
70
|
+
# - str: String to prepend with a namespace
|
71
|
+
#
|
72
|
+
# Returns:
|
73
|
+
# - String with a namespace
|
74
|
+
#
|
75
|
+
def prepend_namespace(str)
|
76
|
+
return "%s:%s" % [DEFAULT_NAMESPACE, str]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Author:: api.dklimkin@gmail.com (Danial Klimkin)
|
1
|
+
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
4
2
|
#
|
5
3
|
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
4
|
#
|
@@ -17,26 +15,35 @@
|
|
17
15
|
# See the License for the specific language governing permissions and
|
18
16
|
# limitations under the License.
|
19
17
|
#
|
20
|
-
#
|
18
|
+
# OAuth request proxy for HTTPI::Request.
|
21
19
|
|
22
|
-
require '
|
20
|
+
require 'oauth/request_proxy/base'
|
21
|
+
require 'httpi/request'
|
23
22
|
|
24
|
-
module
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
23
|
+
module OAuth
|
24
|
+
module RequestProxy
|
25
|
+
class HTTPIRequest < OAuth::RequestProxy::Base
|
26
|
+
proxies HTTPI::Request
|
27
|
+
|
28
|
+
# HTTP method to use.
|
29
|
+
def method
|
30
|
+
return 'POST'
|
31
|
+
end
|
32
|
+
|
33
|
+
# Request URL.
|
34
|
+
def uri
|
35
|
+
request.url.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# Query parameters.
|
39
|
+
def parameters
|
40
|
+
options[:parameters]
|
41
|
+
end
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
# Request body.
|
44
|
+
def body
|
45
|
+
request.body
|
46
|
+
end
|
40
47
|
end
|
41
48
|
end
|
42
49
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# Authors:: api.dklimkin@gmail.com (Danial Klimkin)
|
4
|
+
#
|
5
|
+
# Copyright:: Copyright 2011, Google Inc. All Rights Reserved.
|
6
|
+
#
|
7
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
16
|
+
# implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
# Handles SOAP headers and namespaces definition for OAuth type header.
|
21
|
+
|
22
|
+
require 'oauth'
|
23
|
+
|
24
|
+
require 'ads_common/savon_headers/base_header_handler'
|
25
|
+
require 'ads_common/savon_headers/httpi_request_proxy'
|
26
|
+
|
27
|
+
module AdsCommon
|
28
|
+
module SavonHeaders
|
29
|
+
class OAuthHeaderHandler < BaseHeaderHandler
|
30
|
+
# Enriches soap object with API-specific headers like namespaces, login
|
31
|
+
# credentials etc. Sets the default namespace for the body to the one
|
32
|
+
# specified in initializer.
|
33
|
+
#
|
34
|
+
# Args:
|
35
|
+
# - request: a HTTPI Request for extra configuration
|
36
|
+
# - soap: a Savon soap object to fill fields in
|
37
|
+
# - args: request parameters to adjust for namespaces
|
38
|
+
#
|
39
|
+
# Returns:
|
40
|
+
# - Modified soap structure
|
41
|
+
#
|
42
|
+
def prepare_request(request, soap, args)
|
43
|
+
super(request, soap, args)
|
44
|
+
generate_headers(request, soap)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Generates SOAP request header with login credentials and namespace
|
50
|
+
# definition for OAuth authentication.
|
51
|
+
#
|
52
|
+
# Args:
|
53
|
+
# - request: a HTTPI Request to generate headers for
|
54
|
+
# - soap: a Savon soap object to fill fields in
|
55
|
+
#
|
56
|
+
# Returns:
|
57
|
+
# - Hash containing a header with filled in credentials
|
58
|
+
#
|
59
|
+
def generate_headers(request, soap)
|
60
|
+
headers = @auth_handler.headers(@credential_handler.credentials)
|
61
|
+
request_header = headers.inject({}) do |request_header, (header, value)|
|
62
|
+
if header == :access_token
|
63
|
+
request.headers['Authorization'] =
|
64
|
+
generate_oauth_parameters_string(request, value, soap.endpoint)
|
65
|
+
else
|
66
|
+
request_header[prepend_namespace(header)] = value
|
67
|
+
end
|
68
|
+
request_header
|
69
|
+
end
|
70
|
+
soap.header[prepend_namespace(@element_name)] = request_header
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generates auth string for OAuth method of authentication.
|
74
|
+
#
|
75
|
+
# Args:
|
76
|
+
# - request: a HTTPI::Request to sign
|
77
|
+
# - access_token: an initialized OAuth AccessToken
|
78
|
+
# - url: request URL to generate auth string for
|
79
|
+
#
|
80
|
+
# Returns:
|
81
|
+
# - Authentication string
|
82
|
+
#
|
83
|
+
def generate_oauth_parameters_string(request, access_token, url)
|
84
|
+
request.url = url
|
85
|
+
oauth_params = {:consumer => @auth_handler.get_oauth_consumer(),
|
86
|
+
:token => access_token}
|
87
|
+
oauth_helper = OAuth::Client::Helper.new(request, oauth_params)
|
88
|
+
return oauth_helper.header
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -17,49 +17,29 @@
|
|
17
17
|
# See the License for the specific language governing permissions and
|
18
18
|
# limitations under the License.
|
19
19
|
#
|
20
|
-
# Handles SOAP headers and namespaces definition for Savon SOAP backend.
|
20
|
+
# Handles simple SOAP headers and namespaces definition for Savon SOAP backend.
|
21
|
+
|
22
|
+
require 'ads_common/savon_headers/base_header_handler'
|
21
23
|
|
22
24
|
module AdsCommon
|
23
25
|
module SavonHeaders
|
24
|
-
class SimpleHeaderHandler
|
25
|
-
|
26
|
-
# Default namespace alias
|
27
|
-
DEFAULT_NAMESPACE = 'wsdl'
|
28
|
-
|
29
|
-
# Initializes a header handler.
|
30
|
-
#
|
31
|
-
# Args:
|
32
|
-
# - credential_handler: a header with credential data.
|
33
|
-
# - auth_handler: a header with auth data.
|
34
|
-
# - element_name: an API-specific name of header element.
|
35
|
-
# - namespace: default namespace to use.
|
36
|
-
# - version: services version.
|
37
|
-
def initialize(credential_handler, auth_handler, element_name,
|
38
|
-
namespace, version)
|
39
|
-
@credential_handler = credential_handler
|
40
|
-
@auth_handler = auth_handler
|
41
|
-
@element_name = element_name
|
42
|
-
@namespace = namespace
|
43
|
-
@version = version
|
44
|
-
Savon.configure {|config| config.raise_errors = false}
|
45
|
-
end
|
46
|
-
|
26
|
+
class SimpleHeaderHandler < BaseHeaderHandler
|
47
27
|
# Enriches soap object with API-specific headers like namespaces, login
|
48
28
|
# credentials etc. Sets the default namespace for the body to the one
|
49
29
|
# specified in initializer.
|
50
30
|
#
|
51
31
|
# Args:
|
52
|
-
# -
|
53
|
-
# -
|
32
|
+
# - request: a HTTPI Request for extra configuration (unused)
|
33
|
+
# - soap: a Savon soap object to fill fields in
|
34
|
+
# - args: request parameters to adjust for namespaces
|
54
35
|
#
|
55
36
|
# Returns:
|
56
|
-
# - Modified soap structure
|
57
|
-
|
37
|
+
# - Modified soap structure
|
38
|
+
#
|
39
|
+
def prepare_request(request, soap, args)
|
40
|
+
super(request, soap, args)
|
58
41
|
soap.header[prepend_namespace(@element_name)] =
|
59
42
|
generate_request_header()
|
60
|
-
soap.namespace = @namespace
|
61
|
-
soap.body = args if args
|
62
|
-
soap.input[1] = {:xmlns => @namespace}
|
63
43
|
end
|
64
44
|
|
65
45
|
private
|
@@ -68,29 +48,17 @@ module AdsCommon
|
|
68
48
|
# definition.
|
69
49
|
#
|
70
50
|
# Args:
|
71
|
-
# - None
|
51
|
+
# - None
|
72
52
|
#
|
73
53
|
# Returns:
|
74
|
-
# - Hash containing a header with filled in credentials
|
54
|
+
# - Hash containing a header with filled in credentials
|
55
|
+
#
|
75
56
|
def generate_request_header()
|
76
|
-
|
77
|
-
|
78
|
-
headers = @auth_handler.headers(credentials)
|
79
|
-
headers.each do |header, value|
|
57
|
+
headers = @auth_handler.headers(@credential_handler.credentials)
|
58
|
+
return headers.inject({}) do |request_header, (header, value)|
|
80
59
|
request_header[prepend_namespace(header)] = value
|
60
|
+
request_header
|
81
61
|
end
|
82
|
-
return request_header
|
83
|
-
end
|
84
|
-
|
85
|
-
# Adds namespace to the given string.
|
86
|
-
#
|
87
|
-
# Args:
|
88
|
-
# - str: String to prepend with a namespace.
|
89
|
-
#
|
90
|
-
# Returns:
|
91
|
-
# - String with a namespace.
|
92
|
-
def prepend_namespace(str)
|
93
|
-
return "%s:%s" % [DEFAULT_NAMESPACE, str]
|
94
62
|
end
|
95
63
|
end
|
96
64
|
end
|
@@ -19,18 +19,26 @@
|
|
19
19
|
#
|
20
20
|
# Base class for all generated API services based on Savon backend.
|
21
21
|
|
22
|
-
gem 'savon', '~>0.9.1'
|
23
22
|
require 'savon'
|
24
23
|
|
25
24
|
module AdsCommon
|
26
25
|
class SavonService
|
26
|
+
# Default namespace name.
|
27
|
+
DEFAULT_NAMESPACE = 'wsdl'
|
28
|
+
|
27
29
|
attr_accessor :headerhandler
|
30
|
+
attr_reader :api
|
31
|
+
attr_reader :version
|
32
|
+
attr_reader :namespace
|
28
33
|
|
29
34
|
# Creates a new service.
|
30
|
-
def initialize(endpoint, namespace)
|
35
|
+
def initialize(api, endpoint, namespace, version)
|
31
36
|
if self.class() == AdsCommon::SavonService
|
32
37
|
raise NoMethodError, "Tried to instantiate an abstract class"
|
33
38
|
end
|
39
|
+
@api = api
|
40
|
+
@version = version
|
41
|
+
@namespace = namespace
|
34
42
|
@headerhandler = []
|
35
43
|
@client = Savon::Client.new do |wsdl|
|
36
44
|
wsdl.namespace = namespace
|
@@ -74,34 +82,60 @@ module AdsCommon
|
|
74
82
|
# - resolve xsi:type where required;
|
75
83
|
# - convert some native types to xml.
|
76
84
|
def validate_args(action_name, args)
|
77
|
-
validated_args =
|
85
|
+
validated_args = {}
|
78
86
|
in_params = get_service_registry.get_method_signature(action_name)[:input]
|
79
87
|
in_params.each_with_index do |in_param, index|
|
80
88
|
key = in_param[:name]
|
81
89
|
value = deep_copy(args[index])
|
82
|
-
validated_args[key] = (value.nil?) ?
|
83
|
-
|
90
|
+
validated_args[key] = (value.nil?) ?
|
91
|
+
nil : validate_arg(value, validated_args, key, in_param[:type])
|
84
92
|
end
|
85
93
|
return validated_args
|
86
94
|
end
|
87
95
|
|
88
96
|
# Validates method argument. Runs recursively if hash or array encountered.
|
89
97
|
# Also handles some types that need special conversions.
|
90
|
-
def validate_arg(arg, parent = nil, key = nil)
|
98
|
+
def validate_arg(arg, parent = nil, key = nil, field_type_name = nil)
|
99
|
+
field_type = get_full_type_signature(field_type_name)
|
91
100
|
result = case arg
|
92
|
-
when Hash
|
93
|
-
|
101
|
+
when Hash
|
102
|
+
validate_hash_arg(arg, parent, key, field_type)
|
103
|
+
when Array then arg.map do |item|
|
104
|
+
validate_arg(item, parent, key, field_type_name)
|
105
|
+
end
|
94
106
|
when Time then time_to_xml_hash(arg)
|
95
107
|
else arg
|
96
108
|
end
|
97
109
|
return result
|
98
110
|
end
|
99
111
|
|
112
|
+
# Generates order of XML elements for SOAP request. Returns only items
|
113
|
+
# existing in arg.
|
114
|
+
def generate_order_for_args(arg, fields)
|
115
|
+
all_keys = fields.map {|field| field[:name]}
|
116
|
+
return all_keys & arg.keys
|
117
|
+
end
|
118
|
+
|
100
119
|
# Validates hash argument recursively. Keeps tracking of correct place
|
101
120
|
# for xsi:type and adds is when required.
|
102
|
-
def validate_hash_arg(arg, parent = nil, key = nil)
|
121
|
+
def validate_hash_arg(arg, parent = nil, key = nil, field_type = nil)
|
122
|
+
# Non-default namespace should be used, overriding default.
|
123
|
+
if field_type and field_type.include?(:ns)
|
124
|
+
namespace = get_service_registry.get_namespace(field_type[:ns])
|
125
|
+
add_attribute(parent, prefix_key(key), 'xmlns', namespace)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Handling custom xsi:type.
|
103
129
|
xsi_type = arg.delete('xsi:type') || arg.delete(:xsi_type)
|
104
130
|
if xsi_type
|
131
|
+
xsi_field_type = get_full_type_signature(xsi_type)
|
132
|
+
if xsi_field_type.nil?
|
133
|
+
raise AdsCommon::Errors::ApiException.new(
|
134
|
+
"Incorrect xsi:type specified: %s" % [xsi_type])
|
135
|
+
else
|
136
|
+
# TODO: make sure xsi_type is derived from field_type.
|
137
|
+
field_type = xsi_field_type
|
138
|
+
end
|
105
139
|
if parent and key
|
106
140
|
add_xsi_type(parent, key, xsi_type)
|
107
141
|
else
|
@@ -110,24 +144,71 @@ module AdsCommon
|
|
110
144
|
[xsi_type, parent, key])
|
111
145
|
end
|
112
146
|
end
|
113
|
-
|
114
|
-
|
115
|
-
|
147
|
+
|
148
|
+
# Adding :order! key to keep correct order in SOAP elements.
|
149
|
+
if field_type and field_type.include?(:fields)
|
150
|
+
arg[:order!] =
|
151
|
+
generate_order_for_args(arg, field_type[:fields])
|
152
|
+
end
|
153
|
+
|
154
|
+
# Processing each key-value pair.
|
155
|
+
return arg.inject({}) do |result, (k, v)|
|
156
|
+
if (k == :attributes! or k == :order!)
|
157
|
+
result[k] = v
|
158
|
+
else
|
159
|
+
subfield = (field_type.nil?) ? nil :
|
160
|
+
get_field_by_name(field_type[:fields], k)
|
161
|
+
# Here we will give up if the field is unknown. For full validation
|
162
|
+
# we have to handle nil here.
|
163
|
+
subtype_name, subtype = if subfield and subfield.include?(:type)
|
164
|
+
subtype_name = subfield[:type]
|
165
|
+
subtype = (subtype_name.nil?) ? nil :
|
166
|
+
get_service_registry.get_type_signature(subtype_name)
|
167
|
+
[subtype_name, subtype]
|
168
|
+
end
|
169
|
+
# In case of non-default namespace, the children should be in
|
170
|
+
# overriden namespace but the node has to be in the default.
|
171
|
+
# We also have to fix order! list if we alter the key name.
|
172
|
+
new_key = if (subtype and subtype[:ns])
|
173
|
+
prefixed_key = prefix_key(k)
|
174
|
+
replace_item!(arg[:order!], k, prefixed_key)
|
175
|
+
prefixed_key
|
176
|
+
else
|
177
|
+
k
|
178
|
+
end
|
179
|
+
result[new_key] = validate_arg(v, result, k, subtype_name)
|
180
|
+
end
|
116
181
|
result
|
117
182
|
end
|
118
183
|
end
|
119
184
|
|
185
|
+
# Replaces an item in an array with a different one into the same position.
|
186
|
+
def replace_item!(data, old_item, new_item)
|
187
|
+
data.map! {|item| (item == old_item) ? new_item : item}
|
188
|
+
end
|
189
|
+
|
120
190
|
# Adds ":attributes!" record for Savon to specify xsi:type.
|
121
191
|
def add_xsi_type(parent, key, xsi_type)
|
122
|
-
parent
|
123
|
-
|
124
|
-
|
125
|
-
|
192
|
+
add_attribute(parent, key, 'xsi:type', xsi_type)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Adds Savon attribute for given node, key, name and value.
|
196
|
+
def add_attribute(node, key, name, value)
|
197
|
+
node[:attributes!] ||= {}
|
198
|
+
node[:attributes!][key] ||= {}
|
199
|
+
node[:attributes!][key][name] = value
|
200
|
+
end
|
201
|
+
|
202
|
+
# Prefixes default namespace.
|
203
|
+
def prefix_key(key)
|
204
|
+
return "%s:%s" % [DEFAULT_NAMESPACE, key.to_s.lower_camelcase]
|
126
205
|
end
|
127
206
|
|
128
207
|
# Executes each handler to generate SOAP headers.
|
129
208
|
def set_headers(soap, args)
|
130
|
-
@headerhandler.each
|
209
|
+
@headerhandler.each do |handler|
|
210
|
+
handler.prepare_request(@client.http, soap, args)
|
211
|
+
end
|
131
212
|
end
|
132
213
|
|
133
214
|
# Checks for errors in response and raises appropriate exception.
|
@@ -194,26 +275,22 @@ module AdsCommon
|
|
194
275
|
output_data[field_sym] = normalize_type(output_data[field_sym],
|
195
276
|
field_definition)
|
196
277
|
|
197
|
-
sub_type =
|
198
|
-
|
199
|
-
|
200
|
-
sub_type[:fields]
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
items_list.each do |item|
|
208
|
-
output_data[field_sym] <<
|
209
|
-
normalize_output_field(item, sub_type_field,
|
210
|
-
sub_type_field[:name])
|
211
|
-
end
|
212
|
-
else
|
213
|
-
output_data[field_sym] =
|
214
|
-
normalize_output_field(output_data[field_sym], sub_type_field,
|
278
|
+
sub_type = get_full_type_signature(field_definition[:type])
|
279
|
+
if sub_type and sub_type[:fields]
|
280
|
+
# go recursive
|
281
|
+
sub_type[:fields].each do |sub_type_field|
|
282
|
+
if output_data[field_sym].is_a?(Array)
|
283
|
+
items_list = output_data[field_sym]
|
284
|
+
output_data[field_sym] = []
|
285
|
+
items_list.each do |item|
|
286
|
+
output_data[field_sym] <<
|
287
|
+
normalize_output_field(item, sub_type_field,
|
215
288
|
sub_type_field[:name])
|
216
289
|
end
|
290
|
+
else
|
291
|
+
output_data[field_sym] =
|
292
|
+
normalize_output_field(output_data[field_sym], sub_type_field,
|
293
|
+
sub_type_field[:name])
|
217
294
|
end
|
218
295
|
end
|
219
296
|
end
|
@@ -232,7 +309,8 @@ module AdsCommon
|
|
232
309
|
# If field signature allows an array, forcing array structure even for one
|
233
310
|
# item.
|
234
311
|
if !field[:min_occurs].nil? and
|
235
|
-
(field[:max_occurs]
|
312
|
+
(field[:max_occurs] == :unbounded ||
|
313
|
+
(!field[:max_occurs].nil? and field[:max_occurs] > 1))
|
236
314
|
result = arrayize(result)
|
237
315
|
end
|
238
316
|
return result
|
@@ -259,7 +337,7 @@ module AdsCommon
|
|
259
337
|
|
260
338
|
# Makes sure object is an array.
|
261
339
|
def arrayize(object)
|
262
|
-
return
|
340
|
+
return [] if object.nil?
|
263
341
|
return object.is_a?(Array) ? object : [object]
|
264
342
|
end
|
265
343
|
|
@@ -273,12 +351,12 @@ module AdsCommon
|
|
273
351
|
|
274
352
|
# Returns all inherited fields of superclasses for given type.
|
275
353
|
def implode_parent(data_type)
|
276
|
-
result =
|
277
|
-
if data_type
|
354
|
+
result = []
|
355
|
+
if data_type[:base]
|
278
356
|
parent_type = get_service_registry.get_type_signature(data_type[:base])
|
279
|
-
result += parent_type
|
280
|
-
result += implode_parent(parent_type) if parent_type[:base]
|
357
|
+
result += implode_parent(parent_type)
|
281
358
|
end
|
359
|
+
result += data_type[:fields]
|
282
360
|
return result
|
283
361
|
end
|
284
362
|
|
@@ -286,5 +364,15 @@ module AdsCommon
|
|
286
364
|
def deep_copy(data)
|
287
365
|
return Marshal.load(Marshal.dump(data))
|
288
366
|
end
|
367
|
+
|
368
|
+
# Returns type signature with all inherited fields.
|
369
|
+
def get_full_type_signature(type_name)
|
370
|
+
result = (type_name.nil?) ? nil :
|
371
|
+
get_service_registry.get_type_signature(type_name)
|
372
|
+
if result and result[:base]
|
373
|
+
result[:fields] = implode_parent(result)
|
374
|
+
end
|
375
|
+
return result
|
376
|
+
end
|
289
377
|
end
|
290
378
|
end
|