hs-pact-support 1.17.1

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