soaspec 0.2.24 → 0.2.25
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.
- checksums.yaml +5 -5
- data/.gitignore +15 -15
- data/.gitlab-ci.yml +51 -33
- data/.rspec +3 -3
- data/.rubocop.yml +2 -2
- data/CODE_OF_CONDUCT.md +74 -74
- data/ChangeLog +588 -577
- data/Gemfile +6 -6
- data/LICENSE.txt +21 -21
- data/README.md +230 -230
- data/Rakefile +50 -42
- data/Todo.md +15 -15
- data/exe/soaspec +137 -123
- data/exe/xml_to_yaml_file +42 -42
- data/lib/soaspec.rb +103 -101
- data/lib/soaspec/core_ext/hash.rb +42 -35
- data/lib/soaspec/cucumber/generic_steps.rb +85 -85
- data/lib/soaspec/demo.rb +4 -4
- data/lib/soaspec/exchange/exchange.rb +117 -111
- data/lib/soaspec/exchange/exchange_extractor.rb +83 -83
- data/lib/soaspec/exchange/exchange_properties.rb +26 -26
- data/lib/soaspec/exchange/exchange_repeater.rb +19 -19
- data/lib/soaspec/exchange/request_builder.rb +68 -68
- data/lib/soaspec/exchange/variable_storer.rb +22 -22
- data/lib/soaspec/exchange_handlers/exchange_handler.rb +130 -126
- data/lib/soaspec/exchange_handlers/handler_accessors.rb +130 -130
- data/lib/soaspec/exchange_handlers/request/rest_request.rb +49 -0
- data/lib/soaspec/exchange_handlers/request/soap_request.rb +39 -0
- data/lib/soaspec/exchange_handlers/response_extractor.rb +82 -82
- data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +109 -109
- data/lib/soaspec/exchange_handlers/rest_handler.rb +287 -259
- data/lib/soaspec/exchange_handlers/rest_methods.rb +63 -44
- data/lib/soaspec/exchange_handlers/rest_parameters.rb +90 -86
- data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +40 -21
- data/lib/soaspec/exchange_handlers/soap_handler.rb +239 -235
- data/lib/soaspec/exe_helpers.rb +92 -92
- data/lib/soaspec/generate_server.rb +46 -37
- data/lib/soaspec/generator/.rspec.erb +5 -5
- data/lib/soaspec/generator/.travis.yml.erb +5 -5
- data/lib/soaspec/generator/Gemfile.erb +8 -8
- data/lib/soaspec/generator/README.md.erb +29 -29
- data/lib/soaspec/generator/Rakefile.erb +19 -19
- data/lib/soaspec/generator/config/data/default.yml.erb +2 -2
- data/lib/soaspec/generator/css/bootstrap.css +6833 -6833
- data/lib/soaspec/generator/features/support/env.rb.erb +3 -0
- data/lib/soaspec/generator/generate_exchange.html.erb +47 -35
- data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
- data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
- data/lib/soaspec/generator/lib/new_rest_service.rb.erb +56 -51
- data/lib/soaspec/generator/lib/new_soap_service.rb.erb +29 -29
- data/lib/soaspec/generator/lib/package_service.rb.erb +2 -2
- data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
- data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
- data/lib/soaspec/generator/spec/rest_spec.rb.erb +9 -9
- data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
- data/lib/soaspec/generator/spec/spec_helper.rb.erb +23 -23
- data/lib/soaspec/generator/template/soap_template.xml +6 -6
- data/lib/soaspec/indifferent_hash.rb +7 -7
- data/lib/soaspec/interpreter.rb +39 -39
- data/lib/soaspec/matchers.rb +114 -114
- data/lib/soaspec/not_found_errors.rb +13 -13
- data/lib/soaspec/o_auth2.rb +128 -128
- data/lib/soaspec/soaspec_shared_examples.rb +24 -24
- data/lib/soaspec/spec_logger.rb +122 -121
- data/lib/soaspec/template_reader.rb +28 -28
- data/lib/soaspec/test_server/bank.wsdl +90 -90
- data/lib/soaspec/test_server/get_bank.rb +164 -164
- data/lib/soaspec/test_server/id_manager.rb +39 -39
- data/lib/soaspec/test_server/invoices.rb +27 -27
- data/lib/soaspec/test_server/namespace.xml +14 -14
- data/lib/soaspec/test_server/note.xml +5 -5
- data/lib/soaspec/test_server/puppy_service.rb +19 -19
- data/lib/soaspec/test_server/test_attribute.rb +12 -12
- data/lib/soaspec/test_server/test_namespace.rb +12 -12
- data/lib/soaspec/version.rb +4 -4
- data/lib/soaspec/virtual_server.rb +174 -174
- data/lib/soaspec/wait.rb +41 -41
- data/lib/soaspec/wsdl_generator.rb +215 -215
- data/soaspec.gemspec +56 -53
- data/test.wsdl +116 -116
- data/test.xml +10 -10
- data/test_wsdl.rb +41 -41
- metadata +38 -6
@@ -1,109 +1,109 @@
|
|
1
|
-
module Soaspec
|
2
|
-
# Convenience methods for once off usage of a REST request
|
3
|
-
module RestExchangeFactory
|
4
|
-
# Make REST Exchange with 'post' method within this Handler context
|
5
|
-
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
6
|
-
# @option params [String] :name Name to appear in traffic logs
|
7
|
-
# @option params [Hash] :params Extra parameters (E.g. headers)
|
8
|
-
# @option params [String] :suburl URL appended to base_url of class
|
9
|
-
# @option params [Hash] :q Query for REST
|
10
|
-
# Following are for the body of the request
|
11
|
-
# @option params [Hash] :body Hash to be converted to JSON in request body
|
12
|
-
# @option params [String] :payload String to be passed directly in request body
|
13
|
-
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
14
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
15
|
-
def post(params = {})
|
16
|
-
perform_exchange_with(:post, params)
|
17
|
-
end
|
18
|
-
|
19
|
-
# Make REST Exchange with 'patch' method within this Handler context
|
20
|
-
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
21
|
-
# @option params [String] :name Name to appear in traffic logs
|
22
|
-
# @option params [Hash] :params Extra parameters (E.g. headers)
|
23
|
-
# @option params [String] suburl URL appended to base_url of class
|
24
|
-
# @option params [Hash] :q Query for REST
|
25
|
-
# Following are for the body of the request
|
26
|
-
# @option params [Hash] :body Hash to be converted to JSON in request body
|
27
|
-
# @option params [String] :payload String to be passed directly in request body
|
28
|
-
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
29
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
30
|
-
def patch(params)
|
31
|
-
perform_exchange_with(:patch, params)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Make REST Exchange with 'put' method within this Handler context
|
35
|
-
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
36
|
-
# @option params [String] :name Name to appear in traffic logs
|
37
|
-
# @option params [Hash] :params Extra parameters (E.g. headers)
|
38
|
-
# @option params [String] :suburl URL appended to base_url of class
|
39
|
-
# @option params [Hash] :q Query for REST
|
40
|
-
# Following are for the body of the request
|
41
|
-
# @option params [Hash] :body Hash to be converted to JSON in request body
|
42
|
-
# @option params [String] :payload String to be passed directly in request body
|
43
|
-
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
44
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
45
|
-
def put(params = {})
|
46
|
-
perform_exchange_with(:put, params)
|
47
|
-
end
|
48
|
-
|
49
|
-
# Make REST Exchange with 'get' method within this Handler context.
|
50
|
-
# If merely a string is passed it will be used as the URL appended to base_url (same as suburl). Otherwise a Hash is expected
|
51
|
-
# @param [Hash, String] params Exchange parameters. If String is used it will be for suburl
|
52
|
-
# @option params [String] :name Name to appear in traffic logs
|
53
|
-
# @option params [String] :suburl URL appended to base_url of class
|
54
|
-
# @option params [Hash] :params Extra parameters (E.g. headers)
|
55
|
-
# @option params [Hash] :q Query for REST
|
56
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
57
|
-
def get(params = {})
|
58
|
-
perform_exchange_with(:get, params)
|
59
|
-
end
|
60
|
-
|
61
|
-
# Make REST Exchange with 'delete' method within this Handler context.
|
62
|
-
# If merely a string is passed it will be used as the URL appended to base_url (same as suburl). Otherwise a Hash is expected
|
63
|
-
# @param [Hash, String] params Exchange parameters. If String is used it will be for suburl
|
64
|
-
# @option params [String] :name Name to appear in traffic logs
|
65
|
-
# @option params [String] :suburl URL appended to base_url of class
|
66
|
-
# @option params [Hash] :q Query for REST
|
67
|
-
# @option params [Hash] :params Extra parameters (E.g. headers)
|
68
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
69
|
-
def delete(params = {})
|
70
|
-
perform_exchange_with(:delete, params)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
# Make REST Exchange within this Handler context
|
76
|
-
# @param [Symbol] rest_method HTTP rest method to use
|
77
|
-
# @param [Hash, String] params Exchange parameters.
|
78
|
-
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
79
|
-
def perform_exchange_with(rest_method, params = {})
|
80
|
-
params = determine_params_for(rest_method, params)
|
81
|
-
params[:name] ||= rest_method.to_s
|
82
|
-
exchange_params = { name: params[:name] }
|
83
|
-
if params[:template_name]
|
84
|
-
exchange_params[:template_name] = params[:template_name]
|
85
|
-
params.delete :template_name
|
86
|
-
end
|
87
|
-
new(exchange_params)
|
88
|
-
exchange = Exchange.new(params[:name], method: rest_method, **params)
|
89
|
-
yield exchange if block_given?
|
90
|
-
exchange
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param [Symbol] method HTTP rest method to use
|
94
|
-
# @param [Hash, String] params Exchange parameters.
|
95
|
-
# @return [Hash] Exchange Parameters after setting shorthand parameters
|
96
|
-
def determine_params_for(method, params)
|
97
|
-
return params if params.is_a? Hash
|
98
|
-
|
99
|
-
case method
|
100
|
-
when :get, :delete
|
101
|
-
{ suburl: params.to_s }
|
102
|
-
when :post, :put, :patch
|
103
|
-
{ payload: params.to_s }
|
104
|
-
else
|
105
|
-
raise "'#{params}' needs to be a 'Hash' but is a #{params.class}"
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
1
|
+
module Soaspec
|
2
|
+
# Convenience methods for once off usage of a REST request
|
3
|
+
module RestExchangeFactory
|
4
|
+
# Make REST Exchange with 'post' method within this Handler context
|
5
|
+
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
6
|
+
# @option params [String] :name Name to appear in traffic logs
|
7
|
+
# @option params [Hash] :params Extra parameters (E.g. headers)
|
8
|
+
# @option params [String] :suburl URL appended to base_url of class
|
9
|
+
# @option params [Hash] :q Query for REST
|
10
|
+
# Following are for the body of the request
|
11
|
+
# @option params [Hash] :body Hash to be converted to JSON in request body
|
12
|
+
# @option params [String] :payload String to be passed directly in request body
|
13
|
+
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
14
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
15
|
+
def post(params = {})
|
16
|
+
perform_exchange_with(:post, params)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Make REST Exchange with 'patch' method within this Handler context
|
20
|
+
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
21
|
+
# @option params [String] :name Name to appear in traffic logs
|
22
|
+
# @option params [Hash] :params Extra parameters (E.g. headers)
|
23
|
+
# @option params [String] suburl URL appended to base_url of class
|
24
|
+
# @option params [Hash] :q Query for REST
|
25
|
+
# Following are for the body of the request
|
26
|
+
# @option params [Hash] :body Hash to be converted to JSON in request body
|
27
|
+
# @option params [String] :payload String to be passed directly in request body
|
28
|
+
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
29
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
30
|
+
def patch(params)
|
31
|
+
perform_exchange_with(:patch, params)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Make REST Exchange with 'put' method within this Handler context
|
35
|
+
# @param [Hash, String] params Exchange parameters. If String is used it will be for the request payload
|
36
|
+
# @option params [String] :name Name to appear in traffic logs
|
37
|
+
# @option params [Hash] :params Extra parameters (E.g. headers)
|
38
|
+
# @option params [String] :suburl URL appended to base_url of class
|
39
|
+
# @option params [Hash] :q Query for REST
|
40
|
+
# Following are for the body of the request
|
41
|
+
# @option params [Hash] :body Hash to be converted to JSON in request body
|
42
|
+
# @option params [String] :payload String to be passed directly in request body
|
43
|
+
# @option params [String] :template_name Path to file to be read via ERB and passed in request body
|
44
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
45
|
+
def put(params = {})
|
46
|
+
perform_exchange_with(:put, params)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Make REST Exchange with 'get' method within this Handler context.
|
50
|
+
# If merely a string is passed it will be used as the URL appended to base_url (same as suburl). Otherwise a Hash is expected
|
51
|
+
# @param [Hash, String] params Exchange parameters. If String is used it will be for suburl
|
52
|
+
# @option params [String] :name Name to appear in traffic logs
|
53
|
+
# @option params [String] :suburl URL appended to base_url of class
|
54
|
+
# @option params [Hash] :params Extra parameters (E.g. headers)
|
55
|
+
# @option params [Hash] :q Query for REST
|
56
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
57
|
+
def get(params = {})
|
58
|
+
perform_exchange_with(:get, params)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Make REST Exchange with 'delete' method within this Handler context.
|
62
|
+
# If merely a string is passed it will be used as the URL appended to base_url (same as suburl). Otherwise a Hash is expected
|
63
|
+
# @param [Hash, String] params Exchange parameters. If String is used it will be for suburl
|
64
|
+
# @option params [String] :name Name to appear in traffic logs
|
65
|
+
# @option params [String] :suburl URL appended to base_url of class
|
66
|
+
# @option params [Hash] :q Query for REST
|
67
|
+
# @option params [Hash] :params Extra parameters (E.g. headers)
|
68
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
69
|
+
def delete(params = {})
|
70
|
+
perform_exchange_with(:delete, params)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Make REST Exchange within this Handler context
|
76
|
+
# @param [Symbol] rest_method HTTP rest method to use
|
77
|
+
# @param [Hash, String] params Exchange parameters.
|
78
|
+
# @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
|
79
|
+
def perform_exchange_with(rest_method, params = {})
|
80
|
+
params = determine_params_for(rest_method, params)
|
81
|
+
params[:name] ||= rest_method.to_s
|
82
|
+
exchange_params = { name: params[:name] }
|
83
|
+
if params[:template_name]
|
84
|
+
exchange_params[:template_name] = params[:template_name]
|
85
|
+
params.delete :template_name
|
86
|
+
end
|
87
|
+
new(exchange_params)
|
88
|
+
exchange = Exchange.new(params[:name], method: rest_method, **params)
|
89
|
+
yield exchange if block_given?
|
90
|
+
exchange
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param [Symbol] method HTTP rest method to use
|
94
|
+
# @param [Hash, String] params Exchange parameters.
|
95
|
+
# @return [Hash] Exchange Parameters after setting shorthand parameters
|
96
|
+
def determine_params_for(method, params)
|
97
|
+
return params if params.is_a? Hash
|
98
|
+
|
99
|
+
case method
|
100
|
+
when :get, :delete
|
101
|
+
{ suburl: params.to_s }
|
102
|
+
when :post, :put, :patch
|
103
|
+
{ payload: params.to_s }
|
104
|
+
else
|
105
|
+
raise "'#{params}' needs to be a 'Hash' but is a #{params.class}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -1,259 +1,287 @@
|
|
1
|
-
require_relative 'exchange_handler'
|
2
|
-
require_relative 'rest_parameters'
|
3
|
-
require_relative 'rest_parameters_defaults'
|
4
|
-
require_relative 'rest_exchanger_factory'
|
5
|
-
require_relative '../core_ext/hash'
|
6
|
-
require_relative '../not_found_errors'
|
7
|
-
require_relative 'handler_accessors'
|
8
|
-
require_relative '../interpreter'
|
9
|
-
require_relative 'response_extractor'
|
10
|
-
|
11
|
-
require '
|
12
|
-
require '
|
13
|
-
require '
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# @
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
# @
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
# @return [
|
145
|
-
def
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
# @return [
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
#
|
170
|
-
# @param [
|
171
|
-
# @
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
# @
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
1
|
+
require_relative 'exchange_handler'
|
2
|
+
require_relative 'rest_parameters'
|
3
|
+
require_relative 'rest_parameters_defaults'
|
4
|
+
require_relative 'rest_exchanger_factory'
|
5
|
+
require_relative '../core_ext/hash'
|
6
|
+
require_relative '../not_found_errors'
|
7
|
+
require_relative 'handler_accessors'
|
8
|
+
require_relative '../interpreter'
|
9
|
+
require_relative 'response_extractor'
|
10
|
+
require_relative 'request/rest_request'
|
11
|
+
require 'json'
|
12
|
+
require 'jsonpath'
|
13
|
+
require 'nori'
|
14
|
+
require 'erb'
|
15
|
+
|
16
|
+
module Soaspec
|
17
|
+
# Wraps around Savon client defining default values dependent on the soap request
|
18
|
+
class RestHandler < ExchangeHandler
|
19
|
+
include ResponseExtractor
|
20
|
+
extend Soaspec::RestParameters
|
21
|
+
include Soaspec::RestParametersDefaults
|
22
|
+
extend Soaspec::RestExchangeFactory
|
23
|
+
|
24
|
+
# User used in making API calls
|
25
|
+
attr_accessor :api_username
|
26
|
+
|
27
|
+
# Setup object to handle communicating with a particular SOAP WSDL
|
28
|
+
# @param [Hash] options Options defining REST request. base_url, default_hash
|
29
|
+
def initialize(name = self.class.to_s, options = {})
|
30
|
+
raise "Base URL not set! Please set in class with 'base_url' method" unless base_url_value
|
31
|
+
|
32
|
+
if name.is_a?(Hash) && options == {} # If name is not set, use first parameter as the options hash
|
33
|
+
options = name
|
34
|
+
name = self.class.to_s
|
35
|
+
end
|
36
|
+
super
|
37
|
+
set_remove_keys(options, %i[api_username default_hash template_name])
|
38
|
+
@init_options = options
|
39
|
+
init_merge_options # Call this to verify any issues with options on creating object
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Boolean] Whether REST method should have a payload
|
43
|
+
def payload?(overall_params)
|
44
|
+
case overall_params[:method]
|
45
|
+
when :post, :patch, :put
|
46
|
+
true
|
47
|
+
else
|
48
|
+
false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @todo Use this in actually making the request
|
53
|
+
# @return [RestRequest] Parameters used in making a request
|
54
|
+
def request_parameters(override_parameters)
|
55
|
+
overall_params = interpret_parameters(override_parameters)
|
56
|
+
request = { overall: overall_params, options: init_merge_options }
|
57
|
+
request[:body] = post_data(overall_params) if payload?(overall_params)
|
58
|
+
RestRequest.new(overall_params, init_merge_options, payload?(overall_params) ? post_data(overall_params) : nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Interpret REST parameters given provided parameters and adding defaults, making
|
62
|
+
# transformations
|
63
|
+
#
|
64
|
+
# @param [Hash] request_parameters Parameters used in making a request
|
65
|
+
# @return [Hash] Request parameters merged with default values
|
66
|
+
def interpret_parameters(request_parameters)
|
67
|
+
request_parameters[:params] ||= {}
|
68
|
+
request_parameters[:method] ||= :post
|
69
|
+
request_parameters[:suburl] = request_parameters[:suburl].to_s if request_parameters[:suburl]
|
70
|
+
# Use q for query parameters. Nested :params is ugly, long and unclear
|
71
|
+
request_parameters[:params][:params] = request_parameters[:q] if request_parameters[:q]
|
72
|
+
request_parameters
|
73
|
+
end
|
74
|
+
|
75
|
+
# Used in together with Exchange request that passes such override parameters
|
76
|
+
# @param [Hash] override_parameters Params to characterize REST request
|
77
|
+
# @option override_parameters [Hash] :params Extra parameters (E.g. headers)
|
78
|
+
# @option override_parameters [String] suburl URL appended to base_url of class
|
79
|
+
# @option override_parameters [Hash] :q Query for REST
|
80
|
+
# @option override_parameters [Symbol] :method REST method (:get, :post, :patch, etc)
|
81
|
+
# Following are for the body of the request
|
82
|
+
# @option override_parameters [Hash] :body Hash to be converted to JSON in request body
|
83
|
+
# @option override_parameters [String] :payload String to be passed directly in request body
|
84
|
+
# @option override_parameters [String] :template_name Path to file to be read via ERB and passed in request body
|
85
|
+
# @return [RestClient::Response] Response from making request
|
86
|
+
def make_request(override_parameters)
|
87
|
+
@merged_options ||= init_merge_options # TODO: Is this var needed? Can method be passed to resource creation?
|
88
|
+
test_values = interpret_parameters override_parameters
|
89
|
+
# In order for ERB to be calculated at correct time, the first time request is made, the resource should be created
|
90
|
+
@resource ||= RestClient::Resource.new(ERB.new(base_url_value).result(binding), @merged_options)
|
91
|
+
|
92
|
+
@resource_used = test_values[:suburl] ? @resource[test_values[:suburl]] : @resource
|
93
|
+
|
94
|
+
begin
|
95
|
+
response = case test_values[:method]
|
96
|
+
when :post, :patch, :put
|
97
|
+
Soaspec::SpecLogger.info("request body: #{post_data(test_values)}")
|
98
|
+
@resource_used.send(test_values[:method].to_s, post_data(test_values), test_values[:params])
|
99
|
+
else # :get, :delete
|
100
|
+
@resource_used.send(test_values[:method].to_s, test_values[:params])
|
101
|
+
end
|
102
|
+
rescue RestClient::ExceptionWithResponse => e
|
103
|
+
response = e.response
|
104
|
+
end
|
105
|
+
Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response}"])
|
106
|
+
response
|
107
|
+
end
|
108
|
+
|
109
|
+
# Add values to here when extending this class to have default REST options.
|
110
|
+
# See rest client resource at https://github.com/rest-client/rest-client for details
|
111
|
+
# It's easier to set headers via 'headers' accessor rather than here
|
112
|
+
# @return [Hash] Options adding to & overriding defaults
|
113
|
+
def rest_resource_options
|
114
|
+
{}
|
115
|
+
end
|
116
|
+
|
117
|
+
# Perform ERB on each header value
|
118
|
+
# @return [Hash] Hash from 'rest_client_headers' passed through ERB
|
119
|
+
def parse_headers
|
120
|
+
Hash[rest_client_headers.map do |header_name, header_value|
|
121
|
+
raise ArgumentError, "Header '#{header_name}' is null. Headers are #{rest_client_headers}" if header_value.nil?
|
122
|
+
|
123
|
+
[header_name, ERB.new(header_value).result(binding)]
|
124
|
+
end]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Initialize value of merged options
|
128
|
+
# @return [Hash] Hash of merged options
|
129
|
+
def init_merge_options
|
130
|
+
options = rest_resource_options
|
131
|
+
options.merge! basic_auth_params if respond_to? :basic_auth_params
|
132
|
+
options[:headers] ||= {}
|
133
|
+
options[:headers].merge! parse_headers
|
134
|
+
options[:headers][:authorization] ||= ERB.new('Bearer <%= access_token %>').result(binding) if Soaspec.auto_oauth && respond_to?(:access_token)
|
135
|
+
options.merge(@init_options)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param [Hash] format Format of expected result.
|
139
|
+
# @return [Object] Generic body to be displayed in error messages
|
140
|
+
def response_body(response, format: :hash)
|
141
|
+
extract_hash response
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [Boolean] Whether response body includes String
|
145
|
+
def include_in_body?(response, expected)
|
146
|
+
response.body.include? expected
|
147
|
+
end
|
148
|
+
|
149
|
+
# @@return [Boolean] Whether the request found the desired value or not
|
150
|
+
def found?(response)
|
151
|
+
status_code_for(response) != 404
|
152
|
+
end
|
153
|
+
|
154
|
+
# @return [Boolean] Whether response contains expected value
|
155
|
+
def include_value?(response, expected)
|
156
|
+
extract_hash(response).include_value? expected
|
157
|
+
end
|
158
|
+
|
159
|
+
# @return [Boolean] Whether response body contains expected key
|
160
|
+
def include_key?(response, expected)
|
161
|
+
value_from_path(response, expected)
|
162
|
+
end
|
163
|
+
|
164
|
+
# @return [Integer] HTTP Status code for response
|
165
|
+
def status_code_for(response)
|
166
|
+
response.code
|
167
|
+
end
|
168
|
+
|
169
|
+
# Returns the value at the provided xpath
|
170
|
+
# @param [RestClient::Response] response
|
171
|
+
# @param [String] xpath Path to find elements from
|
172
|
+
# @param [String] attribute Attribute to find path for
|
173
|
+
# @return [Enumerable] Value inside element found through Xpath
|
174
|
+
def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
|
175
|
+
raise ArgumentError unless response && xpath
|
176
|
+
raise "Can't perform XPATH if response is not XML" unless Interpreter.response_type_for(response) == :xml
|
177
|
+
|
178
|
+
xpath = prefix_xpath(xpath, attribute)
|
179
|
+
temp_doc = Nokogiri.parse(response.body).dup
|
180
|
+
if strip_namespaces? && !xpath.include?(':')
|
181
|
+
temp_doc.remove_namespaces!
|
182
|
+
temp_doc.xpath(xpath)
|
183
|
+
else
|
184
|
+
temp_doc.xpath(xpath, temp_doc.collect_namespaces)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [Enumerable] List of values matching JSON path
|
189
|
+
def json_path_values_for(response, path, attribute: nil)
|
190
|
+
raise 'JSON does not support attributes' if attribute
|
191
|
+
|
192
|
+
JsonPath.on(response.body, path)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Calculate all JSON path values based on rules. ',', pascal_case
|
196
|
+
# @param [RestClient::Response] response Response from API
|
197
|
+
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
198
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
199
|
+
# @param [Boolean] not_empty Whether to fail if result is empty
|
200
|
+
# @return [Array] Paths to check as first and matching values (List of values matching JSON Path) as second
|
201
|
+
def calculated_json_path_matches(path, response, attribute, not_empty: false)
|
202
|
+
path = add_pascal_path(path)
|
203
|
+
paths_to_check = path.split(',')
|
204
|
+
paths_to_check = paths_to_check.map { |path_to_check| prefix_json_path(path_to_check) }
|
205
|
+
matching_values = paths_to_check.collect do |path_to_check|
|
206
|
+
json_path_values_for(response, path_to_check, attribute: attribute)
|
207
|
+
end.reject(&:empty?)
|
208
|
+
raise NoElementAtPath, "No value at JSONPath '#{paths_to_check}' in '#{response.body}'" if matching_values.empty? && not_empty
|
209
|
+
|
210
|
+
matching_values.first
|
211
|
+
end
|
212
|
+
|
213
|
+
# Based on a exchange, return the value at the provided xpath
|
214
|
+
# If the path does not begin with a '/', a '//' is added to it
|
215
|
+
# @param [RestClient::Response] response Response from API
|
216
|
+
# @param [Object] path Xpath, JSONPath or other path identifying how to find element
|
217
|
+
# @param [String] attribute Generic attribute to find. Will override path
|
218
|
+
# @return [String] Value at Xpath
|
219
|
+
def value_from_path(response, path, attribute: nil)
|
220
|
+
path = path.to_s
|
221
|
+
case Interpreter.response_type_for(response)
|
222
|
+
when :xml
|
223
|
+
result = xpath_elements_for(response: response, xpath: path, attribute: attribute).first
|
224
|
+
raise NoElementAtPath, "No value at Xpath '#{prefix_xpath(path, attribute)}' in '#{response.body}'" unless result
|
225
|
+
return result.inner_text if attribute.nil?
|
226
|
+
|
227
|
+
return result.attributes[attribute].inner_text
|
228
|
+
when :json
|
229
|
+
matching_values = calculated_json_path_matches(path, response, attribute, not_empty: true)
|
230
|
+
matching_values.first
|
231
|
+
else # Assume this is a String
|
232
|
+
raise NoElementAtPath, 'Response is empty' if response.to_s.empty?
|
233
|
+
|
234
|
+
response.to_s[/#{path}/] # Perform regular expression using path if not XML nor JSON
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @return [Enumerable] List of values returned from path
|
239
|
+
def values_from_path(response, path, attribute: nil)
|
240
|
+
path = path.to_s
|
241
|
+
case Interpreter.response_type_for(response)
|
242
|
+
when :xml
|
243
|
+
xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
|
244
|
+
when :json
|
245
|
+
result = calculated_json_path_matches(path, response, attribute)
|
246
|
+
result || []
|
247
|
+
# json_path_values_for(response, path, attribute: attribute)
|
248
|
+
else
|
249
|
+
raise "Unable to interpret type of #{response.body}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# @return [RestClient::Request] Request of API call. Either intended request or actual request
|
254
|
+
def request(response)
|
255
|
+
return 'Request not yet sent' if response.nil?
|
256
|
+
|
257
|
+
response.request
|
258
|
+
end
|
259
|
+
|
260
|
+
# Work out data to send based upon payload, template_name, or body
|
261
|
+
# @return [String] Payload to send in REST request
|
262
|
+
def post_data(test_values)
|
263
|
+
data = if @request_option == :hash && !test_values[:payload]
|
264
|
+
test_values[:payload] = JSON.generate(hash_used_in_request(test_values[:body])).to_s
|
265
|
+
elsif @request_option == :template
|
266
|
+
test_values = test_values[:body].dup if test_values[:body]
|
267
|
+
test_values = IndifferentHash.new(test_values) # Allow test_values to be either Symbol or String
|
268
|
+
Soaspec::TemplateReader.new.render_body(template_name, binding)
|
269
|
+
else
|
270
|
+
test_values[:payload]
|
271
|
+
end
|
272
|
+
# Soaspec::SpecLogger.info "Request Empty for '#{@request_option}'" if data.strip.empty?
|
273
|
+
data
|
274
|
+
end
|
275
|
+
|
276
|
+
# @param [Hash] override_hash Values to override default hash with
|
277
|
+
# @return [Hash] Hash used in REST request based on data conversion
|
278
|
+
def hash_used_in_request(override_hash)
|
279
|
+
request = override_hash ? @default_hash.merge(override_hash) : @default_hash
|
280
|
+
if pascal_keys?
|
281
|
+
request.map { |k, v| [convert_to_pascal_case(k.to_s), v] }.to_h
|
282
|
+
else
|
283
|
+
request
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|