soaspec 0.2.23 → 0.2.24

Sign up to get free protection for your applications and to get access to all the features.
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