google-ads-common 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|