soaspec 0.2.23 → 0.2.24

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -15
  3. data/.gitlab-ci.yml +33 -33
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +2 -2
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/ChangeLog +577 -573
  8. data/Gemfile +6 -6
  9. data/LICENSE.txt +21 -21
  10. data/README.md +230 -230
  11. data/Rakefile +42 -42
  12. data/Todo.md +15 -15
  13. data/exe/soaspec +123 -123
  14. data/exe/xml_to_yaml_file +42 -42
  15. data/lib/soaspec.rb +101 -101
  16. data/lib/soaspec/core_ext/hash.rb +35 -35
  17. data/lib/soaspec/cucumber/generic_steps.rb +85 -85
  18. data/lib/soaspec/demo.rb +4 -4
  19. data/lib/soaspec/exchange/exchange.rb +111 -111
  20. data/lib/soaspec/exchange/exchange_extractor.rb +83 -83
  21. data/lib/soaspec/exchange/exchange_properties.rb +26 -26
  22. data/lib/soaspec/exchange/exchange_repeater.rb +19 -19
  23. data/lib/soaspec/exchange/request_builder.rb +68 -68
  24. data/lib/soaspec/exchange/variable_storer.rb +22 -22
  25. data/lib/soaspec/exchange_handlers/exchange_handler.rb +126 -126
  26. data/lib/soaspec/exchange_handlers/handler_accessors.rb +130 -130
  27. data/lib/soaspec/exchange_handlers/response_extractor.rb +82 -82
  28. data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +109 -109
  29. data/lib/soaspec/exchange_handlers/rest_handler.rb +259 -259
  30. data/lib/soaspec/exchange_handlers/rest_methods.rb +44 -44
  31. data/lib/soaspec/exchange_handlers/rest_parameters.rb +86 -86
  32. data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +21 -21
  33. data/lib/soaspec/exchange_handlers/soap_handler.rb +235 -235
  34. data/lib/soaspec/exe_helpers.rb +92 -92
  35. data/lib/soaspec/generate_server.rb +37 -37
  36. data/lib/soaspec/generator/.rspec.erb +5 -5
  37. data/lib/soaspec/generator/.travis.yml.erb +5 -5
  38. data/lib/soaspec/generator/Gemfile.erb +8 -8
  39. data/lib/soaspec/generator/README.md.erb +29 -29
  40. data/lib/soaspec/generator/Rakefile.erb +19 -19
  41. data/lib/soaspec/generator/config/data/default.yml.erb +2 -2
  42. data/lib/soaspec/generator/css/bootstrap.css +6833 -6833
  43. data/lib/soaspec/generator/generate_exchange.html.erb +35 -35
  44. data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
  45. data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
  46. data/lib/soaspec/generator/lib/new_rest_service.rb.erb +51 -51
  47. data/lib/soaspec/generator/lib/new_soap_service.rb.erb +29 -29
  48. data/lib/soaspec/generator/lib/package_service.rb.erb +2 -2
  49. data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
  50. data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
  51. data/lib/soaspec/generator/spec/rest_spec.rb.erb +9 -9
  52. data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
  53. data/lib/soaspec/generator/spec/spec_helper.rb.erb +23 -23
  54. data/lib/soaspec/generator/template/soap_template.xml +6 -6
  55. data/lib/soaspec/indifferent_hash.rb +7 -7
  56. data/lib/soaspec/interpreter.rb +39 -39
  57. data/lib/soaspec/matchers.rb +114 -114
  58. data/lib/soaspec/not_found_errors.rb +13 -13
  59. data/lib/soaspec/o_auth2.rb +128 -128
  60. data/lib/soaspec/soaspec_shared_examples.rb +24 -24
  61. data/lib/soaspec/spec_logger.rb +121 -121
  62. data/lib/soaspec/template_reader.rb +28 -28
  63. data/lib/soaspec/test_server/bank.wsdl +90 -90
  64. data/lib/soaspec/test_server/get_bank.rb +164 -164
  65. data/lib/soaspec/test_server/id_manager.rb +39 -39
  66. data/lib/soaspec/test_server/invoices.rb +27 -27
  67. data/lib/soaspec/test_server/namespace.xml +14 -14
  68. data/lib/soaspec/test_server/note.xml +5 -5
  69. data/lib/soaspec/test_server/puppy_service.rb +19 -19
  70. data/lib/soaspec/test_server/test_attribute.rb +12 -12
  71. data/lib/soaspec/test_server/test_namespace.rb +12 -12
  72. data/lib/soaspec/version.rb +4 -3
  73. data/lib/soaspec/virtual_server.rb +174 -174
  74. data/lib/soaspec/wait.rb +41 -41
  75. data/lib/soaspec/wsdl_generator.rb +215 -215
  76. data/soaspec.gemspec +53 -53
  77. data/test.wsdl +116 -116
  78. data/test.xml +10 -10
  79. data/test_wsdl.rb +41 -41
  80. metadata +3 -4
@@ -1,23 +1,23 @@
1
-
2
- require 'soaspec'
3
- require 'require_all'
4
- require_all 'lib'
5
- require 'data_magic'
6
-
7
- include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
8
- <% if @type == 'rest' %>
9
- include Soaspec::RestMethods
10
- <% end %>
11
-
12
- RSpec.configure do |config|
13
- # This will make backtrace much shorter by removing many lines from rspec failure message
14
- config.backtrace_exclusion_patterns = [
15
- /rspec/
16
- ]
17
- <% if @virtual %>
18
- # Close test server after all RSpec tests have run
19
- config.after(:suite) do
20
- Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
21
- end
22
- <% end %>
23
- end
1
+
2
+ require 'soaspec'
3
+ require 'require_all'
4
+ require_all 'lib'
5
+ require 'data_magic'
6
+
7
+ include DataMagic # Used as example of loading data smartly. Use 'data_for' method to load yml data
8
+ <% if @type == 'rest' %>
9
+ include Soaspec::RestMethods
10
+ <% end %>
11
+
12
+ RSpec.configure do |config|
13
+ # This will make backtrace much shorter by removing many lines from rspec failure message
14
+ config.backtrace_exclusion_patterns = [
15
+ /rspec/
16
+ ]
17
+ <% if @virtual %>
18
+ # Close test server after all RSpec tests have run
19
+ config.after(:suite) do
20
+ Process.kill(:QUIT, ENV['test_server_pid'].to_i) if ENV['test_server_pid'] && !ENV['leave_server_running']
21
+ end
22
+ <% end %>
23
+ end
@@ -1,7 +1,7 @@
1
- <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
2
- <env:Body>
3
- <tns:getBank>
4
- <tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
5
- </tns:getBank>
6
- </env:Body>
1
+ <env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://thomas-bayer.com/blz/" xmlns:env="http://www.w3.org/2003/05/soap-envelope">
2
+ <env:Body>
3
+ <tns:getBank>
4
+ <tns:blz><%= test_values[:blz] || '70070010' %></tns:blz>
5
+ </tns:getBank>
6
+ </env:Body>
7
7
  </env:Envelope>
@@ -1,7 +1,7 @@
1
- require 'hashie'
2
-
3
- # Hash that allows accessing hash with either string or Hash
4
- class IndifferentHash < Hash
5
- include Hashie::Extensions::MergeInitializer
6
- include Hashie::Extensions::IndifferentAccess
7
- end
1
+ require 'hashie'
2
+
3
+ # Hash that allows accessing hash with either string or Hash
4
+ class IndifferentHash < Hash
5
+ include Hashie::Extensions::MergeInitializer
6
+ include Hashie::Extensions::IndifferentAccess
7
+ end
@@ -1,39 +1,39 @@
1
- # Help interpret the general type of a particular object
2
- class Interpreter
3
- class << self
4
- # @param [Object] response API response
5
- # @return [Symbol] Type of provided response
6
- def response_type_for(response)
7
- @response = response
8
- if @response.is_a? String
9
- if xml?
10
- :xml
11
- elsif json?
12
- :json
13
- else
14
- :string
15
- end
16
- elsif response.is_a? Hash
17
- :hash
18
- elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
19
- :xml
20
- else
21
- :unknown
22
- end
23
- end
24
-
25
- # @return [Boolean] Whether valid XML
26
- def xml?
27
- Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
28
- rescue Nokogiri::XML::SyntaxError
29
- false
30
- end
31
-
32
- # @return [Boolean] Whether valid JSON
33
- def json?
34
- JSON.parse(@response)
35
- rescue JSON::ParserError
36
- false
37
- end
38
- end
39
- end
1
+ # Help interpret the general type of a particular object
2
+ class Interpreter
3
+ class << self
4
+ # @param [Object] response API response
5
+ # @return [Symbol] Type of provided response
6
+ def response_type_for(response)
7
+ @response = response
8
+ if @response.is_a? String
9
+ if xml?
10
+ :xml
11
+ elsif json?
12
+ :json
13
+ else
14
+ :string
15
+ end
16
+ elsif response.is_a? Hash
17
+ :hash
18
+ elsif response.is_a?(Nokogiri::XML::NodeSet) || response.is_a?(Nokogiri::XML::Document)
19
+ :xml
20
+ else
21
+ :unknown
22
+ end
23
+ end
24
+
25
+ # @return [Boolean] Whether valid XML
26
+ def xml?
27
+ Nokogiri::XML(@response) { |config| config.options = Nokogiri::XML::ParseOptions::STRICT }
28
+ rescue Nokogiri::XML::SyntaxError
29
+ false
30
+ end
31
+
32
+ # @return [Boolean] Whether valid JSON
33
+ def json?
34
+ JSON.parse(@response)
35
+ rescue JSON::ParserError
36
+ false
37
+ end
38
+ end
39
+ end
@@ -1,114 +1,114 @@
1
- require_relative 'core_ext/hash'
2
- require_relative 'not_found_errors'
3
-
4
- # Whether response has any element with the provided value
5
- RSpec::Matchers.define :contain_value do |expected|
6
- match do |actual|
7
- expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
8
- end
9
-
10
- failure_message do |actual|
11
- "expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
12
- end
13
- end
14
-
15
- # Whether substring exists in body of response (more general than above)
16
- RSpec::Matchers.define :include_in_body do |expected|
17
- match do |actual|
18
- expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
19
- end
20
-
21
- failure_message do |actual|
22
- "expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
23
- end
24
- end
25
-
26
- # Whether an element exists at expected xpath
27
- RSpec::Matchers.define :have_element_at_path do |xpath|
28
- match do |object|
29
- # Object like `response` returns the Exchange object from which a path can be obtained
30
- exchange = object.respond_to?(:exchange) ? object.exchange : object
31
- expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
32
- end
33
-
34
- # TODO: Would be better to print failure message
35
- failure_message do |object|
36
- # Object like `response` returns the Exchange object from which a path can be obtained
37
- exchange = object.respond_to?(:exchange) ? object.exchange : object
38
- "expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
39
- end
40
- end
41
-
42
- RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
43
- RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
44
-
45
- # Whether an element at xpath (defined by key) has value (defined by value).
46
- # @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
47
- RSpec::Matchers.define :have_xpath_value do |expected_hash|
48
- match do |object|
49
- # Object like `response` returns the Exchange object from which a path can be obtained
50
- exchange = object.respond_to?(:exchange) ? object.exchange : object
51
- expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
52
- expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
53
- end
54
-
55
- failure_message do |actual|
56
- "expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
57
- end
58
- end
59
-
60
- RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
61
-
62
- RSpec::Matchers.define :be_found do
63
- match do |exchange|
64
- expect(exchange.exchange_handler.found?(exchange.response)).to be true
65
- end
66
-
67
- failure_message do |exchange|
68
- "expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
69
- end
70
- end
71
-
72
- # Whether response has successful status code and correct mandatory elements and values
73
- RSpec::Matchers.define :be_successful do
74
- # @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
75
- # @return [Exchange] Exchange to use
76
- def exchange_from(actual)
77
- actual.respond_to?(:exchange) ? actual.exchange : actual
78
- end
79
-
80
- # @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
81
- # @return [Array] List of errors when checking Exchange response is successful
82
- def collect_errors(exchange)
83
- failure_list = []
84
- failure_list << "#{exchange.status_code} not valid status code" unless (200..299).cover?(exchange.status_code)
85
- exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
86
- begin
87
- exchange[mandatory_element_path]
88
- rescue NoElementAtPath => error
89
- failure_list << error.message
90
- end
91
- end
92
- exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
93
- failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
94
- end
95
- exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
96
- failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
97
- end
98
- failure_list
99
- end
100
-
101
- match do |actual|
102
- exchange = exchange_from actual
103
- failure_list = collect_errors exchange
104
- raise failure_list.to_s unless failure_list.empty?
105
- true
106
- end
107
-
108
- match_when_negated do |actual|
109
- exchange = exchange_from actual
110
- failure_list = collect_errors exchange
111
- raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
112
- true
113
- end
114
- end
1
+ require_relative 'core_ext/hash'
2
+ require_relative 'not_found_errors'
3
+
4
+ # Whether response has any element with the provided value
5
+ RSpec::Matchers.define :contain_value do |expected|
6
+ match do |actual|
7
+ expect(actual.exchange_handler.include_value?(actual.response, expected)).to be true
8
+ end
9
+
10
+ failure_message do |actual|
11
+ "expected that #{actual.exchange_handler.response_body(actual.response, format: :hash)} would contain value #{expected}"
12
+ end
13
+ end
14
+
15
+ # Whether substring exists in body of response (more general than above)
16
+ RSpec::Matchers.define :include_in_body do |expected|
17
+ match do |actual|
18
+ expect(actual.exchange_handler.include_in_body?(actual.response, expected)).to be true
19
+ end
20
+
21
+ failure_message do |actual|
22
+ "expected that #{actual.exchange_handler.response_body(actual.response, format: :raw)} would contain value #{expected}"
23
+ end
24
+ end
25
+
26
+ # Whether an element exists at expected xpath
27
+ RSpec::Matchers.define :have_element_at_path do |xpath|
28
+ match do |object|
29
+ # Object like `response` returns the Exchange object from which a path can be obtained
30
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
31
+ expect { exchange[xpath] }.to_not raise_error # Error will be raised if Path returns no value
32
+ end
33
+
34
+ # TODO: Would be better to print failure message
35
+ failure_message do |object|
36
+ # Object like `response` returns the Exchange object from which a path can be obtained
37
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
38
+ "expected that #{exchange.exchange_handler.response_body(exchange.response, format: :raw)} would have element at path '#{xpath}'"
39
+ end
40
+ end
41
+
42
+ RSpec::Matchers.alias_matcher :have_element_at_xpath, :have_element_at_path
43
+ RSpec::Matchers.alias_matcher :contain_key, :have_element_at_path
44
+
45
+ # Whether an element at xpath (defined by key) has value (defined by value).
46
+ # @param [Hash] expected_hash Xpath => Value pair (e.g. '//xmlns:GetWeatherResult' => 'Data Not Found')
47
+ RSpec::Matchers.define :have_xpath_value do |expected_hash|
48
+ match do |object|
49
+ # Object like `response` returns the Exchange object from which a path can be obtained
50
+ exchange = object.respond_to?(:exchange) ? object.exchange : object
51
+ expected_hash = Hash[*expected_hash.flatten] if expected_hash.is_a?(Array) # For some reason Array was occuring
52
+ expect(exchange[expected_hash.keys.first]).to eq expected_hash.values.first
53
+ end
54
+
55
+ failure_message do |actual|
56
+ "expected that xpath '#{expected_hash.keys.first}' has value '#{expected_hash.values.first}' but was '#{actual[expected_hash.keys.first]}'"
57
+ end
58
+ end
59
+
60
+ RSpec::Matchers.alias_matcher :have_jsonpath_value, :have_xpath_value
61
+
62
+ RSpec::Matchers.define :be_found do
63
+ match do |exchange|
64
+ expect(exchange.exchange_handler.found?(exchange.response)).to be true
65
+ end
66
+
67
+ failure_message do |exchange|
68
+ "expected result #{exchange.response} to be found. Status code is #{exchange.response.code}"
69
+ end
70
+ end
71
+
72
+ # Whether response has successful status code and correct mandatory elements and values
73
+ RSpec::Matchers.define :be_successful do
74
+ # @param [Exchange, RestClient::Response] actual Object that returns Exchange or is Exchange
75
+ # @return [Exchange] Exchange to use
76
+ def exchange_from(actual)
77
+ actual.respond_to?(:exchange) ? actual.exchange : actual
78
+ end
79
+
80
+ # @param [Exchange, RestClient::Response] exchange Object that returns Exchange or is Exchange
81
+ # @return [Array] List of errors when checking Exchange response is successful
82
+ def collect_errors(exchange)
83
+ failure_list = []
84
+ failure_list << "#{exchange.status_code} not valid status code" unless (200..299).cover?(exchange.status_code)
85
+ exchange.exchange_handler.expected_mandatory_elements.each do |mandatory_element_path|
86
+ begin
87
+ exchange[mandatory_element_path]
88
+ rescue NoElementAtPath => error
89
+ failure_list << error.message
90
+ end
91
+ end
92
+ exchange.exchange_handler.expected_mandatory_xpath_values.each do |path, value|
93
+ failure_list << "Expected value at xpath '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
94
+ end
95
+ exchange.exchange_handler.expected_mandatory_json_values.each do |path, value|
96
+ failure_list << "Expected value at json '#{path}' to be '#{value}' but was '#{exchange[path]}'" unless exchange[path] == value
97
+ end
98
+ failure_list
99
+ end
100
+
101
+ match do |actual|
102
+ exchange = exchange_from actual
103
+ failure_list = collect_errors exchange
104
+ raise failure_list.to_s unless failure_list.empty?
105
+ true
106
+ end
107
+
108
+ match_when_negated do |actual|
109
+ exchange = exchange_from actual
110
+ failure_list = collect_errors exchange
111
+ raise "Expected failure. Status code is #{exchange.status_code}" if failure_list.empty?
112
+ true
113
+ end
114
+ end
@@ -1,13 +1,13 @@
1
- # Raised to represent when there's no element at an Xpath
2
- class NoElementAtPath < StandardError
3
- def initialize(msg = 'No element at path found')
4
- super
5
- end
6
- end
7
-
8
- # Did not find any element by provided key in the Hash
9
- class NoElementInHash < StandardError
10
- def initialize(msg = 'No element in Hash found')
11
- super
12
- end
13
- end
1
+ # Raised to represent when there's no element at an Xpath
2
+ class NoElementAtPath < StandardError
3
+ def initialize(msg = 'No element at path found')
4
+ super
5
+ end
6
+ end
7
+
8
+ # Did not find any element by provided key in the Hash
9
+ class NoElementInHash < StandardError
10
+ def initialize(msg = 'No element in Hash found')
11
+ super
12
+ end
13
+ end
@@ -1,128 +1,128 @@
1
- require 'erb'
2
-
3
- module Soaspec
4
- # Handles working with OAuth2
5
- class OAuth2
6
- # How often to refresh access token
7
- @refresh_token = :always
8
- # List of access tokens. They are mapped according to the OAuth parameters used
9
- @access_tokens = {}
10
- # List of instance URLs. They are mapped according to the OAuth parameters used
11
- @instance_urls = {}
12
- class << self
13
- # Default token url used across entire suite
14
- attr_accessor :token_url
15
- # @attr [Symbol] refresh_token How often to refresh access token
16
- # Values are:
17
- # * :always - (Default) Request token from token url every time it is needed
18
- # * :once - Request token once for the entire execution of the suite
19
- attr_accessor :refresh_token
20
- # @attr [Hash] access_tokens List of access tokens. They are mapped according to the OAuth parameters used
21
- attr_accessor :access_tokens
22
- # List of URLs to that define the instance of an application
23
- attr_accessor :instance_urls
24
- # Specify whether to see params sent to and retrieved from oauth. This will put password in log file, only recommended for debugging
25
- attr_writer :debug_oauth
26
-
27
- # @return [Boolean] Whether to see params sent to & received from oauth URL
28
- def debug_oauth?
29
- @debug_oauth || false
30
- end
31
- end
32
-
33
- # @attr [Hash] OAuth parameters
34
- attr_accessor :params
35
- # @attr [Integer] Count of tries to obtain access token
36
- attr_accessor :retry_count
37
-
38
- # @param [Hash] params_sent Parameters to make OAuth request
39
- # @option params_sent [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
40
- # @option params_sent [client_id] Client ID
41
- # @option params_sent [client_secret] Client Secret
42
- # @option params_sent [username] Username used in password grant
43
- # @option params_sent [password] Password used in password grant
44
- # @option params_sent [security_token] Security Token used in password grant
45
- # @param [String] api_username Username to use which can be set by Soaspec::ExchangeHandler
46
- def initialize(params_sent, api_username = nil)
47
- self.retry_count = 0 # No initial tries at getting access token
48
- params = params_sent.transform_keys_to_symbols
49
- params[:token_url] ||= Soaspec::OAuth2.token_url
50
- raise ArgumentError, 'client_id and client_secret not set' unless params[:client_id] && params[:client_secret]
51
- raise ArgumentError, 'token_url mandatory' unless params[:token_url]
52
-
53
- self.params = params
54
- params[:username] = api_username || ERB.new(params[:username]).result(binding) if params[:username]
55
- params[:security_token] = ERB.new(params[:security_token]).result(binding) if params[:security_token]
56
- params[:token_url] = ERB.new(params[:token_url]).result(binding) if params[:token_url]
57
- params[:password] = ERB.new(params[:password]).result(binding) if params[:password]
58
- end
59
-
60
- # Retrieve whether to debug oauth parameters based on global settings
61
- # @return [Boolean] Whether to see params sent to & received from oauth URL
62
- def debug_oauth?
63
- self.class.debug_oauth?
64
- end
65
-
66
- # Retrieve instance_url according to access token response.
67
- # Some applications have a different instance
68
- # It's assumed this will be constant for a set of oauth parameters
69
- # @return [String] Instance url
70
- def instance_url
71
- Soaspec::OAuth2.instance_urls[params] ||= response['instance_url']
72
- end
73
-
74
- # @return [String] Existing or new access token, dependent on refresh_token attribute
75
- def access_token
76
- Soaspec::SpecLogger.info request_message
77
- case Soaspec::OAuth2.refresh_token
78
- when :once
79
- Soaspec::OAuth2.access_tokens[params] ||= response['access_token']
80
- else # Default is :always
81
- response['access_token']
82
- end
83
- end
84
-
85
- # @return [Hash] Hash containing access token parameters
86
- def response
87
- Soaspec::SpecLogger.info "using oauth_params: #{params}" if debug_oauth?
88
- response = RestClient.post(params[:token_url], payload, cache_control: 'no_cache', verify_ssl: false)
89
- rescue RestClient::Exception => error
90
- Soaspec::SpecLogger.info(["oauth_error: #{error.message}", "oauth_response: #{error.response}"])
91
- self.retry_count += 1
92
- sleep 0.1 # Wait if a bit before retying obtaining access token
93
- retry if retry_count < 3
94
- raise error
95
- else
96
- Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response.body}"]) if debug_oauth?
97
- JSON.parse(response)
98
- end
99
-
100
- # @return [String] String to represent OAuth for logging logs
101
- def request_message
102
- if debug_oauth?
103
- "request_params: #{payload}"
104
- else
105
- params[:username] ? "User '#{params[:username]}'" : 'client_credentials'
106
- end
107
- end
108
-
109
- # @return [String] Password to use in OAuth request
110
- def password
111
- params[:security_token] ? (params[:password] + params[:security_token]) : params[:password]
112
- end
113
-
114
- # Payload to add to o-auth request dependent on params provided
115
- # @return [Hash] Payload for retrieving OAuth access token
116
- def payload
117
- payload = { client_id: params[:client_id], client_secret: params[:client_secret] }
118
- payload.merge(if params[:password] && params[:username]
119
- {
120
- grant_type: 'password', username: params[:username],
121
- password: password, multipart: true
122
- }
123
- else
124
- { grant_type: 'client_credentials' }
125
- end)
126
- end
127
- end
128
- end
1
+ require 'erb'
2
+
3
+ module Soaspec
4
+ # Handles working with OAuth2
5
+ class OAuth2
6
+ # How often to refresh access token
7
+ @refresh_token = :always
8
+ # List of access tokens. They are mapped according to the OAuth parameters used
9
+ @access_tokens = {}
10
+ # List of instance URLs. They are mapped according to the OAuth parameters used
11
+ @instance_urls = {}
12
+ class << self
13
+ # Default token url used across entire suite
14
+ attr_accessor :token_url
15
+ # @attr [Symbol] refresh_token How often to refresh access token
16
+ # Values are:
17
+ # * :always - (Default) Request token from token url every time it is needed
18
+ # * :once - Request token once for the entire execution of the suite
19
+ attr_accessor :refresh_token
20
+ # @attr [Hash] access_tokens List of access tokens. They are mapped according to the OAuth parameters used
21
+ attr_accessor :access_tokens
22
+ # List of URLs to that define the instance of an application
23
+ attr_accessor :instance_urls
24
+ # Specify whether to see params sent to and retrieved from oauth. This will put password in log file, only recommended for debugging
25
+ attr_writer :debug_oauth
26
+
27
+ # @return [Boolean] Whether to see params sent to & received from oauth URL
28
+ def debug_oauth?
29
+ @debug_oauth || false
30
+ end
31
+ end
32
+
33
+ # @attr [Hash] OAuth parameters
34
+ attr_accessor :params
35
+ # @attr [Integer] Count of tries to obtain access token
36
+ attr_accessor :retry_count
37
+
38
+ # @param [Hash] params_sent Parameters to make OAuth request
39
+ # @option params_sent [token_url] URL to retrieve OAuth token from. @Note this can be set globally instead of here
40
+ # @option params_sent [client_id] Client ID
41
+ # @option params_sent [client_secret] Client Secret
42
+ # @option params_sent [username] Username used in password grant
43
+ # @option params_sent [password] Password used in password grant
44
+ # @option params_sent [security_token] Security Token used in password grant
45
+ # @param [String] api_username Username to use which can be set by Soaspec::ExchangeHandler
46
+ def initialize(params_sent, api_username = nil)
47
+ self.retry_count = 0 # No initial tries at getting access token
48
+ params = params_sent.transform_keys_to_symbols
49
+ params[:token_url] ||= Soaspec::OAuth2.token_url
50
+ raise ArgumentError, 'client_id and client_secret not set' unless params[:client_id] && params[:client_secret]
51
+ raise ArgumentError, 'token_url mandatory' unless params[:token_url]
52
+
53
+ self.params = params
54
+ params[:username] = api_username || ERB.new(params[:username]).result(binding) if params[:username]
55
+ params[:security_token] = ERB.new(params[:security_token]).result(binding) if params[:security_token]
56
+ params[:token_url] = ERB.new(params[:token_url]).result(binding) if params[:token_url]
57
+ params[:password] = ERB.new(params[:password]).result(binding) if params[:password]
58
+ end
59
+
60
+ # Retrieve whether to debug oauth parameters based on global settings
61
+ # @return [Boolean] Whether to see params sent to & received from oauth URL
62
+ def debug_oauth?
63
+ self.class.debug_oauth?
64
+ end
65
+
66
+ # Retrieve instance_url according to access token response.
67
+ # Some applications have a different instance
68
+ # It's assumed this will be constant for a set of oauth parameters
69
+ # @return [String] Instance url
70
+ def instance_url
71
+ Soaspec::OAuth2.instance_urls[params] ||= response['instance_url']
72
+ end
73
+
74
+ # @return [String] Existing or new access token, dependent on refresh_token attribute
75
+ def access_token
76
+ Soaspec::SpecLogger.info request_message
77
+ case Soaspec::OAuth2.refresh_token
78
+ when :once
79
+ Soaspec::OAuth2.access_tokens[params] ||= response['access_token']
80
+ else # Default is :always
81
+ response['access_token']
82
+ end
83
+ end
84
+
85
+ # @return [Hash] Hash containing access token parameters
86
+ def response
87
+ Soaspec::SpecLogger.info "using oauth_params: #{params}" if debug_oauth?
88
+ response = RestClient.post(params[:token_url], payload, cache_control: 'no_cache', verify_ssl: false)
89
+ rescue RestClient::Exception => error
90
+ Soaspec::SpecLogger.info(["oauth_error: #{error.message}", "oauth_response: #{error.response}"])
91
+ self.retry_count += 1
92
+ sleep 0.1 # Wait if a bit before retying obtaining access token
93
+ retry if retry_count < 3
94
+ raise error
95
+ else
96
+ Soaspec::SpecLogger.info(["response_headers: #{response.headers}", "response_body: #{response.body}"]) if debug_oauth?
97
+ JSON.parse(response)
98
+ end
99
+
100
+ # @return [String] String to represent OAuth for logging logs
101
+ def request_message
102
+ if debug_oauth?
103
+ "request_params: #{payload}"
104
+ else
105
+ params[:username] ? "User '#{params[:username]}'" : 'client_credentials'
106
+ end
107
+ end
108
+
109
+ # @return [String] Password to use in OAuth request
110
+ def password
111
+ params[:security_token] ? (params[:password] + params[:security_token]) : params[:password]
112
+ end
113
+
114
+ # Payload to add to o-auth request dependent on params provided
115
+ # @return [Hash] Payload for retrieving OAuth access token
116
+ def payload
117
+ payload = { client_id: params[:client_id], client_secret: params[:client_secret] }
118
+ payload.merge(if params[:password] && params[:username]
119
+ {
120
+ grant_type: 'password', username: params[:username],
121
+ password: password, multipart: true
122
+ }
123
+ else
124
+ { grant_type: 'client_credentials' }
125
+ end)
126
+ end
127
+ end
128
+ end