hs-pact-support 1.17.1

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 (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +620 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +5 -0
  5. data/lib/pact/array_like.rb +49 -0
  6. data/lib/pact/configuration.rb +193 -0
  7. data/lib/pact/consumer/request.rb +27 -0
  8. data/lib/pact/consumer_contract/consumer_contract.rb +97 -0
  9. data/lib/pact/consumer_contract/file_name.rb +22 -0
  10. data/lib/pact/consumer_contract/headers.rb +51 -0
  11. data/lib/pact/consumer_contract/http_consumer_contract_parser.rb +37 -0
  12. data/lib/pact/consumer_contract/interaction.rb +81 -0
  13. data/lib/pact/consumer_contract/interaction_parser.rb +23 -0
  14. data/lib/pact/consumer_contract/interaction_v2_parser.rb +57 -0
  15. data/lib/pact/consumer_contract/interaction_v3_parser.rb +92 -0
  16. data/lib/pact/consumer_contract/pact_file.rb +157 -0
  17. data/lib/pact/consumer_contract/provider_state.rb +34 -0
  18. data/lib/pact/consumer_contract/query.rb +138 -0
  19. data/lib/pact/consumer_contract/query_hash.rb +89 -0
  20. data/lib/pact/consumer_contract/query_string.rb +51 -0
  21. data/lib/pact/consumer_contract/request.rb +83 -0
  22. data/lib/pact/consumer_contract/response.rb +58 -0
  23. data/lib/pact/consumer_contract/service_consumer.rb +28 -0
  24. data/lib/pact/consumer_contract/service_provider.rb +28 -0
  25. data/lib/pact/consumer_contract/string_with_matching_rules.rb +17 -0
  26. data/lib/pact/consumer_contract.rb +1 -0
  27. data/lib/pact/errors.rb +21 -0
  28. data/lib/pact/helpers.rb +60 -0
  29. data/lib/pact/http/authorization_header_redactor.rb +32 -0
  30. data/lib/pact/logging.rb +14 -0
  31. data/lib/pact/matchers/actual_type.rb +16 -0
  32. data/lib/pact/matchers/base_difference.rb +39 -0
  33. data/lib/pact/matchers/differ.rb +153 -0
  34. data/lib/pact/matchers/difference.rb +13 -0
  35. data/lib/pact/matchers/difference_indicator.rb +26 -0
  36. data/lib/pact/matchers/embedded_diff_formatter.rb +60 -0
  37. data/lib/pact/matchers/expected_type.rb +35 -0
  38. data/lib/pact/matchers/extract_diff_messages.rb +76 -0
  39. data/lib/pact/matchers/index_not_found.rb +15 -0
  40. data/lib/pact/matchers/list_diff_formatter.rb +103 -0
  41. data/lib/pact/matchers/matchers.rb +285 -0
  42. data/lib/pact/matchers/multipart_form_diff_formatter.rb +41 -0
  43. data/lib/pact/matchers/no_diff_at_index.rb +18 -0
  44. data/lib/pact/matchers/regexp_difference.rb +13 -0
  45. data/lib/pact/matchers/type_difference.rb +16 -0
  46. data/lib/pact/matchers/unexpected_index.rb +11 -0
  47. data/lib/pact/matchers/unexpected_key.rb +11 -0
  48. data/lib/pact/matchers/unix_diff_formatter.rb +157 -0
  49. data/lib/pact/matchers.rb +1 -0
  50. data/lib/pact/matching_rules/extract.rb +91 -0
  51. data/lib/pact/matching_rules/jsonpath.rb +58 -0
  52. data/lib/pact/matching_rules/merge.rb +125 -0
  53. data/lib/pact/matching_rules/v3/extract.rb +94 -0
  54. data/lib/pact/matching_rules/v3/merge.rb +141 -0
  55. data/lib/pact/matching_rules.rb +30 -0
  56. data/lib/pact/reification.rb +56 -0
  57. data/lib/pact/rspec.rb +51 -0
  58. data/lib/pact/shared/active_support_support.rb +65 -0
  59. data/lib/pact/shared/dsl.rb +76 -0
  60. data/lib/pact/shared/form_differ.rb +32 -0
  61. data/lib/pact/shared/jruby_support.rb +18 -0
  62. data/lib/pact/shared/json_differ.rb +10 -0
  63. data/lib/pact/shared/key_not_found.rb +15 -0
  64. data/lib/pact/shared/multipart_form_differ.rb +16 -0
  65. data/lib/pact/shared/null_expectation.rb +31 -0
  66. data/lib/pact/shared/request.rb +106 -0
  67. data/lib/pact/shared/text_differ.rb +11 -0
  68. data/lib/pact/something_like.rb +49 -0
  69. data/lib/pact/specification_version.rb +18 -0
  70. data/lib/pact/support/version.rb +5 -0
  71. data/lib/pact/support.rb +12 -0
  72. data/lib/pact/symbolize_keys.rb +13 -0
  73. data/lib/pact/term.rb +85 -0
  74. data/lib/tasks/pact.rake +29 -0
  75. metadata +327 -0
@@ -0,0 +1,49 @@
1
+ require 'pact/symbolize_keys'
2
+ module Pact
3
+ class ArrayLike
4
+ include SymbolizeKeys
5
+
6
+ attr_reader :contents
7
+ attr_reader :min
8
+
9
+ def initialize contents, options = {}
10
+ @contents = contents
11
+ @min = options[:min] || 1
12
+ end
13
+
14
+ def to_hash
15
+ {
16
+ :json_class => self.class.name,
17
+ :contents => contents,
18
+ :min => min
19
+ }
20
+ end
21
+
22
+ def as_json opts = {}
23
+ to_hash
24
+ end
25
+
26
+ def to_json opts = {}
27
+ as_json.to_json opts
28
+ end
29
+
30
+ def self.json_create hash
31
+ symbolized_hash = symbolize_keys(hash)
32
+ new(symbolized_hash[:contents], {min: symbolized_hash[:min]})
33
+ end
34
+
35
+ def eq other
36
+ self == other
37
+ end
38
+
39
+ def == other
40
+ other.is_a?(ArrayLike) && other.contents == self.contents && other.min == self.min
41
+ end
42
+
43
+ def generate
44
+ min.times.collect{ Pact::Reification.from_term contents }
45
+ end
46
+ end
47
+ end
48
+
49
+
@@ -0,0 +1,193 @@
1
+ require 'pact/matchers/embedded_diff_formatter'
2
+ require 'pact/matchers/unix_diff_formatter'
3
+ require 'pact/matchers/list_diff_formatter'
4
+ require 'pact/matchers/multipart_form_diff_formatter'
5
+ require 'pact/shared/json_differ'
6
+ require 'pact/shared/text_differ'
7
+ require 'pact/shared/form_differ'
8
+ require 'pact/shared/multipart_form_differ'
9
+
10
+
11
+ module Pact
12
+
13
+ class Configuration
14
+
15
+ DIFF_FORMATTERS = {
16
+ :embedded => Pact::Matchers::EmbeddedDiffFormatter,
17
+ :unix => Pact::Matchers::UnixDiffFormatter,
18
+ :list => Pact::Matchers::ListDiffFormatter
19
+ }
20
+
21
+
22
+ class NilMatcher
23
+ def self.=~ other
24
+ other == nil ? 0 : nil
25
+ end
26
+ end
27
+
28
+ DIFF_FORMATTER_REGISTRATIONS = [
29
+ [/multipart\/form-data/, Pact::Matchers::MultipartFormDiffFormatter],
30
+ [/.*/, Pact::Matchers::UnixDiffFormatter],
31
+ [NilMatcher, Pact::Matchers::UnixDiffFormatter]
32
+ ]
33
+
34
+ DIFFERS = [
35
+ [/json/, Pact::JsonDiffer],
36
+ [/application\/x\-www\-form\-urlencoded/, Pact::FormDiffer],
37
+ [/multipart\/form-data/, Pact::MultipartFormDiffer],
38
+ [NilMatcher, Pact::TextDiffer],
39
+ [/.*/, Pact::TextDiffer]
40
+ ]
41
+
42
+
43
+ DEFAULT_DIFFER = Pact::TextDiffer
44
+
45
+ attr_accessor :pact_dir
46
+ attr_accessor :log_dir
47
+ attr_accessor :tmp_dir
48
+
49
+ attr_writer :logger
50
+
51
+ attr_accessor :error_stream
52
+ attr_accessor :output_stream
53
+ attr_accessor :pactfile_write_order
54
+ attr_accessor :treat_all_number_classes_as_equivalent # when using type based matching
55
+
56
+ def self.default_configuration
57
+ c = Configuration.new
58
+ c.pact_dir = File.expand_path('./spec/pacts')
59
+ c.tmp_dir = File.expand_path('./tmp/pacts')
60
+ c.log_dir = default_log_dir
61
+
62
+ c.output_stream = $stdout
63
+ c.error_stream = $stderr
64
+ c.pactfile_write_order = :chronological
65
+ c.treat_all_number_classes_as_equivalent = true
66
+
67
+ c
68
+ end
69
+
70
+ def initialize
71
+ @differ_registrations = []
72
+ @diff_formatter_registrations = []
73
+ end
74
+
75
+ def logger
76
+ @logger ||= create_logger
77
+ end
78
+
79
+ # Should this be deprecated in favour of register_diff_formatter???
80
+ def diff_formatter= diff_formatter
81
+ register_diff_formatter(/.*/, diff_formatter)
82
+ register_diff_formatter(nil, diff_formatter)
83
+ end
84
+
85
+ def register_diff_formatter content_type, diff_formatter
86
+ key = content_type_regexp_for content_type
87
+ @diff_formatter_registrations << [key, diff_formatter_for(diff_formatter)]
88
+ end
89
+
90
+ def diff_formatter_for_content_type content_type
91
+ diff_formatter_registrations.find{ | registration | registration.first =~ content_type }.last
92
+ end
93
+
94
+ def register_body_differ content_type, differ
95
+ key = content_type_regexp_for content_type
96
+ validate_differ differ
97
+ @differ_registrations << [key, differ]
98
+ end
99
+
100
+ def body_differ_for_content_type content_type
101
+ differ_registrations
102
+ .find{ | registration | registration.first =~ content_type }
103
+ .tap do |it|
104
+ if content_type.nil? && it.last == Pact::TextDiffer
105
+ error_stream.puts "WARN: No content type found, performing text diff on body"
106
+ logger.warn "No content type found, performing text diff on body"
107
+ end
108
+ end.last
109
+ end
110
+
111
+ def log_path
112
+ log_dir + "/pact.log"
113
+ end
114
+
115
+ def color_enabled
116
+ # Can't use ||= when the variable might be false, it will execute the expression if it's false
117
+ defined?(@color_enabled) ? @color_enabled : true
118
+ end
119
+
120
+ def color_enabled= color_enabled
121
+ @color_enabled = color_enabled
122
+ end
123
+
124
+ private
125
+
126
+ def diff_formatter_for input
127
+ if DIFF_FORMATTERS[input]
128
+ DIFF_FORMATTERS[input]
129
+ elsif input.respond_to?(:call)
130
+ input
131
+ else
132
+ raise "Pact diff_formatter needs to respond to call, or be in the preconfigured list: #{DIFF_FORMATTERS.keys}"
133
+ end
134
+ end
135
+
136
+ def validate_differ differ
137
+ if !differ.respond_to?(:call)
138
+ raise "Pact.configuration.register_body_differ expects a differ that is a lamda or a class/object that responds to call."
139
+ end
140
+ end
141
+
142
+ def content_type_regexp_for content_type
143
+ case content_type
144
+ when String then Regexp.new(/^#{Regexp.escape(content_type)}$/)
145
+ when Regexp then content_type
146
+ when nil then NilMatcher
147
+ else
148
+ raise "Invalid content type used to register a differ (#{content_type.inspect}). Please use a Regexp or a String."
149
+ end
150
+ end
151
+
152
+ def differ_registrations
153
+ @differ_registrations + DIFFERS
154
+ end
155
+
156
+ def diff_formatter_registrations
157
+ @diff_formatter_registrations + DIFF_FORMATTER_REGISTRATIONS
158
+ end
159
+
160
+ def self.default_log_dir
161
+ File.expand_path("./log")
162
+ end
163
+
164
+ #Would love a better way of determining this! It sure won't work on windows.
165
+ def is_rake_running?
166
+ `ps -ef | grep rake | grep #{Process.ppid} | grep -v 'grep'`.size > 0
167
+ end
168
+
169
+ def create_logger
170
+ FileUtils::mkdir_p log_dir
171
+ logger = ::Logger.new(log_path)
172
+ logger.level = ::Logger::DEBUG
173
+ logger
174
+ rescue Errno::EROFS
175
+ # So we can run on RunKit
176
+ logger = ::Logger.new($stdout)
177
+ logger.level = ::Logger::DEBUG
178
+ logger
179
+ end
180
+ end
181
+
182
+ def self.configuration
183
+ @configuration ||= Configuration.default_configuration
184
+ end
185
+
186
+ def self.configure
187
+ yield configuration
188
+ end
189
+
190
+ def self.clear_configuration
191
+ @configuration = nil
192
+ end
193
+ end
@@ -0,0 +1,27 @@
1
+ require 'pact/shared/request'
2
+ require 'pact/shared/key_not_found'
3
+
4
+ module Pact
5
+ module Consumer
6
+ module Request
7
+ class Actual < Pact::Request::Base
8
+
9
+ def self.from_hash(hash)
10
+ sym_hash = symbolize_keys hash
11
+ method = sym_hash.fetch(:method)
12
+ path = sym_hash.fetch(:path)
13
+ query = sym_hash.fetch(:query)
14
+ headers = sym_hash.fetch(:headers)
15
+ body = sym_hash.fetch(:body, nil)
16
+ new(method, path, headers, body, query)
17
+ end
18
+
19
+ protected
20
+
21
+ def self.key_not_found
22
+ nil
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,97 @@
1
+ require 'pact/logging'
2
+ require 'pact/something_like'
3
+ require 'pact/symbolize_keys'
4
+ require 'pact/term'
5
+ require 'pact/shared/active_support_support'
6
+ require 'date'
7
+ require 'json/add/regexp'
8
+ require 'open-uri'
9
+ require 'pact/consumer_contract/service_consumer'
10
+ require 'pact/consumer_contract/service_provider'
11
+ require 'pact/consumer_contract/interaction'
12
+ require 'pact/consumer_contract/pact_file'
13
+ require 'pact/consumer_contract/http_consumer_contract_parser'
14
+
15
+ module Pact
16
+
17
+ class UnrecognizePactFormatError < ::Pact::Error; end
18
+
19
+ class ConsumerContract
20
+
21
+ include SymbolizeKeys
22
+ include Logging
23
+ include PactFile
24
+
25
+ attr_accessor :interactions
26
+ attr_accessor :consumer
27
+ attr_accessor :provider
28
+
29
+ def initialize(attributes = {})
30
+ @interactions = attributes[:interactions] || []
31
+ @consumer = attributes[:consumer]
32
+ @provider = attributes[:provider]
33
+ end
34
+
35
+ def self.add_parser consumer_contract_parser
36
+ parsers.unshift(consumer_contract_parser)
37
+ end
38
+
39
+ def self.parsers
40
+ @parsers ||= [Pact::HttpConsumerContractParser.new]
41
+ end
42
+
43
+ def self.from_hash(hash)
44
+ parsers.each do | parser |
45
+ return parser.call(hash) if parser.can_parse?(hash)
46
+ end
47
+ raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact format: #{hash}")
48
+ end
49
+
50
+ def self.from_json string
51
+ deserialised_object = JSON.load(maintain_backwards_compatiblity_with_producer_keys(string))
52
+ from_hash(deserialised_object)
53
+ end
54
+
55
+ def self.from_uri uri, options = {}
56
+ from_json(Pact::PactFile.read(uri, options))
57
+ rescue UnrecognizePactFormatError
58
+ raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact format. Please check that #{uri} is a valid pact file.")
59
+ end
60
+
61
+ def self.maintain_backwards_compatiblity_with_producer_keys string
62
+ string.gsub('"producer":', '"provider":').gsub('"producer_state":', '"provider_state":') if string
63
+ end
64
+
65
+ def find_interaction criteria
66
+ interactions = find_interactions criteria
67
+ if interactions.size == 0
68
+ raise Pact::Error.new("Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.")
69
+ elsif interactions.size > 1
70
+ raise Pact::Error.new("Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.")
71
+ end
72
+ interactions.first
73
+ end
74
+
75
+ def find_interactions criteria
76
+ interactions.select{ | interaction| interaction.matches_criteria?(criteria)}
77
+ end
78
+
79
+ def each
80
+ interactions.each do | interaction |
81
+ yield interaction
82
+ end
83
+ end
84
+
85
+ def writable_interactions
86
+ interactions.reject do |interaction|
87
+ # For the sake of backwards compatibility, only reject interactions where
88
+ # write_to_pact is explicitly set to false
89
+ interaction.respond_to?(:metadata) &&
90
+ !interaction.metadata.nil? &&
91
+ interaction.metadata.key?(:write_to_pact) &&
92
+ interaction.metadata[:write_to_pact] == false
93
+ end
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,22 @@
1
+ module Pact
2
+ module FileName
3
+ extend self
4
+
5
+ def file_name consumer_name, provider_name, options = {}
6
+ pid = options[:unique] ? "-#{Process.pid}" : ''
7
+ "#{filenamify(consumer_name)}-#{filenamify(provider_name)}#{pid}.json"
8
+ end
9
+
10
+ def file_path consumer_name, provider_name, pact_dir = Pact.configuration.pact_dir, options = {}
11
+ File.join(windows_safe(pact_dir), file_name(consumer_name, provider_name, options))
12
+ end
13
+
14
+ def filenamify name
15
+ name.downcase.gsub(/\s/, '_')
16
+ end
17
+
18
+ def windows_safe(pact_dir)
19
+ pact_dir.gsub("\\", "/")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ module Pact
2
+
3
+ class DuplicateHeaderError < StandardError; end
4
+ class InvalidHeaderNameTypeError < StandardError; end
5
+
6
+ class Headers < Hash
7
+
8
+ def initialize hash = {}
9
+ hash.each_pair do | key, value |
10
+ check_for_invalid key
11
+ self[find_matching_key(key)] = value
12
+ end
13
+ self.freeze
14
+ end
15
+
16
+ def [] key
17
+ super(find_matching_key(key))
18
+ end
19
+
20
+ def fetch *args, &block
21
+ args[0] = find_matching_key(args[0]) if args.first
22
+ super(*args, &block)
23
+ end
24
+
25
+ def key? key
26
+ super(find_matching_key(key))
27
+ end
28
+
29
+ alias_method :has_key?, :key?
30
+ alias_method :include?, :key?
31
+
32
+ private
33
+
34
+ def find_matching_key key
35
+ key = key.to_s
36
+ match = keys.find { |k| k.downcase == key.downcase }
37
+ match.nil? ? key : match
38
+ end
39
+
40
+ def check_for_invalid key
41
+ unless (String === key || Symbol === key)
42
+ raise InvalidHeaderNameTypeError.new "Header name (#{key}) must be a String or a Symbol."
43
+ end
44
+ if key? key
45
+ raise DuplicateHeaderError.new "Duplicate header found (#{find_matching_key(key)} and #{key}. Please use a comma separated single value when multiple headers with the same name are required."
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,37 @@
1
+ require 'pact/specification_version'
2
+
3
+ module Pact
4
+ class HttpConsumerContractParser
5
+ include SymbolizeKeys
6
+
7
+ def call(hash)
8
+ hash = symbolize_keys(hash)
9
+ v = pact_specification_version(hash)
10
+ options = { pact_specification_version: v }
11
+
12
+ if v.after? 3
13
+ Pact.configuration.error_stream.puts "WARN: This code only knows how to parse v3 pacts, attempting to parse v#{options[:pact_specification_version]} pact using v3 code."
14
+ end
15
+
16
+ interactions = hash[:interactions].each_with_index.collect { |hash, index| Interaction.from_hash({ index: index }.merge(hash), options) }
17
+ ConsumerContract.new(
18
+ :consumer => ServiceConsumer.from_hash(hash[:consumer]),
19
+ :provider => ServiceProvider.from_hash(hash[:provider]),
20
+ :interactions => interactions
21
+ )
22
+ end
23
+
24
+ def pact_specification_version hash
25
+ # TODO handle all 3 ways of defining this...
26
+ # metadata.pactSpecificationVersion
27
+ maybe_pact_specification_version_1 = hash[:metadata] && hash[:metadata]['pactSpecification'] && hash[:metadata]['pactSpecification']['version']
28
+ maybe_pact_specification_version_2 = hash[:metadata] && hash[:metadata]['pactSpecificationVersion']
29
+ pact_specification_version = maybe_pact_specification_version_1 || maybe_pact_specification_version_2
30
+ pact_specification_version ? Pact::SpecificationVersion.new(pact_specification_version) : Pact::SpecificationVersion::NIL_VERSION
31
+ end
32
+
33
+ def can_parse?(hash)
34
+ hash.key?('interactions') || hash.key?(:interactions)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,81 @@
1
+ require 'pact/shared/active_support_support'
2
+ require 'pact/consumer_contract/interaction_parser'
3
+
4
+ module Pact
5
+ class Interaction
6
+ include ActiveSupportSupport
7
+
8
+ attr_accessor :description, :request, :response, :provider_state, :provider_states, :metadata, :_id, :index
9
+
10
+ def initialize attributes = {}
11
+ @description = attributes[:description]
12
+ @request = attributes[:request]
13
+ @response = attributes[:response]
14
+ @provider_state = attributes[:provider_state] || attributes[:providerState]
15
+ @provider_states = attributes[:provider_states]
16
+ @metadata = attributes[:metadata]
17
+ @_id = attributes[:_id]
18
+ @index = attributes[:index]
19
+ end
20
+
21
+ def self.from_hash hash, options = {}
22
+ InteractionParser.call(hash, options)
23
+ end
24
+
25
+ def to_hash
26
+ h = { description: description }
27
+
28
+ if provider_states
29
+ h[:provider_states] = provider_states.collect(&:to_hash)
30
+ else
31
+ h[:provider_state] = provider_state
32
+ end
33
+
34
+ h[:request] = request.to_hash
35
+ h[:response] = response.to_hash
36
+ h[:metadata] = metadata
37
+ h
38
+ end
39
+
40
+ def http?
41
+ true
42
+ end
43
+
44
+ def validate!
45
+ raise Pact::InvalidInteractionError.new(self) unless description && request && response
46
+ end
47
+
48
+ def matches_criteria? criteria
49
+ criteria.each do | key, value |
50
+ unless match_criterion self.send(key.to_s), value
51
+ return false
52
+ end
53
+ end
54
+ true
55
+ end
56
+
57
+ def match_criterion target, criterion
58
+ target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
59
+ end
60
+
61
+ def == other
62
+ other.is_a?(Interaction) && to_hash == other.to_hash
63
+ end
64
+
65
+ def eq? other
66
+ self == other
67
+ end
68
+
69
+ def description_with_provider_state_quoted
70
+ provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\""
71
+ end
72
+
73
+ def request_modifies_resource_without_checking_response_body?
74
+ request.modifies_resource? && response.body_allows_any_value?
75
+ end
76
+
77
+ def to_s
78
+ to_hash.to_s
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,23 @@
1
+ require 'pact/specification_version'
2
+ require 'pact/consumer_contract/interaction_v2_parser'
3
+ require 'pact/consumer_contract/interaction_v3_parser'
4
+
5
+ module Pact
6
+ class InteractionParser
7
+ def self.call hash, options = {}
8
+ pact_specification_version = options[:pact_specification_version] || Pact::SpecificationVersion::NIL_VERSION
9
+ case pact_specification_version.major
10
+ when nil, 0, 1, 2 then parse_v2_interaction(hash, pact_specification_version: pact_specification_version)
11
+ else parse_v3_interaction(hash, pact_specification_version: pact_specification_version)
12
+ end
13
+ end
14
+
15
+ def self.parse_v2_interaction hash, options
16
+ InteractionV2Parser.call(hash, options)
17
+ end
18
+
19
+ def self.parse_v3_interaction hash, options
20
+ InteractionV3Parser.call(hash, options)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,57 @@
1
+ require 'pact/consumer_contract/request'
2
+ require 'pact/consumer_contract/response'
3
+ require 'pact/consumer_contract/provider_state'
4
+ require 'pact/consumer_contract/query'
5
+ require 'pact/symbolize_keys'
6
+ require 'pact/matching_rules'
7
+ require 'pact/errors'
8
+
9
+ module Pact
10
+ class InteractionV2Parser
11
+
12
+ include SymbolizeKeys
13
+
14
+ def self.call hash, options
15
+ request = parse_request(hash['request'], options)
16
+ response = parse_response(hash['response'], options)
17
+ provider_states = parse_provider_states(hash['providerState'] || hash['provider_state'])
18
+ metadata = parse_metadata(hash['metadata'])
19
+ Interaction.new(symbolize_keys(hash).merge(request: request,
20
+ response: response,
21
+ provider_states: provider_states,
22
+ metadata: metadata))
23
+ end
24
+
25
+ def self.parse_request request_hash, options
26
+ original_query_string = request_hash['query']
27
+ query_is_string = original_query_string.is_a?(String)
28
+ if query_is_string
29
+ request_hash = request_hash.dup
30
+ request_hash['query'] = Pact::Query.parse_string(request_hash['query'])
31
+ end
32
+ # The query has to be a hash at this stage for the matching rules to be applied
33
+ request_hash = Pact::MatchingRules.merge(request_hash, request_hash['matchingRules'], options)
34
+ # The original query string needs to be passed in to the constructor so it can be used
35
+ # when the request is replayed. Otherwise, we loose the square brackets because they get lost
36
+ # in the translation between string => structured object, as we don't know/store which
37
+ # query string convention was used.
38
+ if query_is_string
39
+ request_hash['query'] = Pact::QueryHash.new(request_hash['query'], original_query_string, Pact::Query.parsed_as_nested?(request_hash['query']))
40
+ end
41
+ request = Pact::Request::Expected.from_hash(request_hash)
42
+ end
43
+
44
+ def self.parse_response response_hash, options
45
+ response_hash = Pact::MatchingRules.merge(response_hash, response_hash['matchingRules'], options)
46
+ Pact::Response.from_hash(response_hash)
47
+ end
48
+
49
+ def self.parse_provider_states provider_state_name
50
+ provider_state_name ? [Pact::ProviderState.new(provider_state_name)] : []
51
+ end
52
+
53
+ def self.parse_metadata metadata_hash
54
+ symbolize_keys(metadata_hash)
55
+ end
56
+ end
57
+ end