pact-v2 2.0.0.pre.preview1

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1321 -0
  3. data/LICENSE.txt +23 -0
  4. data/bin/pact +4 -0
  5. data/lib/pact/cli/generate_pact_docs.rb +4 -0
  6. data/lib/pact/cli/run_pact_verification.rb +99 -0
  7. data/lib/pact/cli/spec_criteria.rb +26 -0
  8. data/lib/pact/cli.rb +45 -0
  9. data/lib/pact/consumer/configuration/configuration_extensions.rb +90 -0
  10. data/lib/pact/consumer/configuration/dsl.rb +11 -0
  11. data/lib/pact/consumer/configuration/mock_service.rb +112 -0
  12. data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
  13. data/lib/pact/consumer/configuration/service_provider.rb +40 -0
  14. data/lib/pact/consumer/configuration.rb +10 -0
  15. data/lib/pact/consumer/consumer_contract_builder.rb +82 -0
  16. data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
  17. data/lib/pact/consumer/interaction_builder.rb +45 -0
  18. data/lib/pact/consumer/rspec.rb +35 -0
  19. data/lib/pact/consumer/spec_hooks.rb +40 -0
  20. data/lib/pact/consumer/world.rb +37 -0
  21. data/lib/pact/consumer.rb +7 -0
  22. data/lib/pact/doc/README.md +13 -0
  23. data/lib/pact/doc/doc_file.rb +40 -0
  24. data/lib/pact/doc/generate.rb +11 -0
  25. data/lib/pact/doc/generator.rb +82 -0
  26. data/lib/pact/doc/interaction_view_model.rb +124 -0
  27. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +68 -0
  28. data/lib/pact/doc/markdown/generator.rb +26 -0
  29. data/lib/pact/doc/markdown/index_renderer.rb +43 -0
  30. data/lib/pact/doc/markdown/interaction.erb +14 -0
  31. data/lib/pact/doc/markdown/interaction_renderer.rb +43 -0
  32. data/lib/pact/doc/sort_interactions.rb +16 -0
  33. data/lib/pact/hal/authorization_header_redactor.rb +32 -0
  34. data/lib/pact/hal/entity.rb +110 -0
  35. data/lib/pact/hal/http_client.rb +128 -0
  36. data/lib/pact/hal/link.rb +112 -0
  37. data/lib/pact/hal/non_json_entity.rb +28 -0
  38. data/lib/pact/hash_refinements.rb +17 -0
  39. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +112 -0
  40. data/lib/pact/pact_broker/fetch_pacts.rb +103 -0
  41. data/lib/pact/pact_broker/notices.rb +34 -0
  42. data/lib/pact/pact_broker/pact_selection_description.rb +66 -0
  43. data/lib/pact/pact_broker.rb +25 -0
  44. data/lib/pact/project_root.rb +7 -0
  45. data/lib/pact/provider/configuration/configuration_extension.rb +69 -0
  46. data/lib/pact/provider/configuration/dsl.rb +18 -0
  47. data/lib/pact/provider/configuration/message_provider_dsl.rb +63 -0
  48. data/lib/pact/provider/configuration/pact_verification.rb +48 -0
  49. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +126 -0
  50. data/lib/pact/provider/configuration/service_provider_config.rb +32 -0
  51. data/lib/pact/provider/configuration/service_provider_dsl.rb +107 -0
  52. data/lib/pact/provider/configuration.rb +7 -0
  53. data/lib/pact/provider/context.rb +0 -0
  54. data/lib/pact/provider/help/console_text.rb +76 -0
  55. data/lib/pact/provider/help/content.rb +38 -0
  56. data/lib/pact/provider/help/pact_diff.rb +43 -0
  57. data/lib/pact/provider/help/prompt_text.rb +49 -0
  58. data/lib/pact/provider/help/write.rb +56 -0
  59. data/lib/pact/provider/matchers/messages.rb +66 -0
  60. data/lib/pact/provider/pact_helper_locator.rb +24 -0
  61. data/lib/pact/provider/pact_source.rb +40 -0
  62. data/lib/pact/provider/pact_spec_runner.rb +188 -0
  63. data/lib/pact/provider/pact_uri.rb +55 -0
  64. data/lib/pact/provider/pact_verification.rb +17 -0
  65. data/lib/pact/provider/print_missing_provider_states.rb +35 -0
  66. data/lib/pact/provider/request.rb +77 -0
  67. data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
  68. data/lib/pact/provider/rspec/calculate_exit_code.rb +18 -0
  69. data/lib/pact/provider/rspec/custom_options_file +0 -0
  70. data/lib/pact/provider/rspec/formatter_rspec_2.rb +76 -0
  71. data/lib/pact/provider/rspec/formatter_rspec_3.rb +195 -0
  72. data/lib/pact/provider/rspec/json_formatter.rb +100 -0
  73. data/lib/pact/provider/rspec/matchers.rb +80 -0
  74. data/lib/pact/provider/rspec/pact_broker_formatter.rb +76 -0
  75. data/lib/pact/provider/rspec.rb +234 -0
  76. data/lib/pact/provider/state/provider_state.rb +180 -0
  77. data/lib/pact/provider/state/provider_state_configured_modules.rb +15 -0
  78. data/lib/pact/provider/state/provider_state_manager.rb +42 -0
  79. data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
  80. data/lib/pact/provider/state/set_up.rb +13 -0
  81. data/lib/pact/provider/state/tear_down.rb +13 -0
  82. data/lib/pact/provider/test_methods.rb +77 -0
  83. data/lib/pact/provider/verification_report.rb +36 -0
  84. data/lib/pact/provider/verification_results/create.rb +88 -0
  85. data/lib/pact/provider/verification_results/publish.rb +143 -0
  86. data/lib/pact/provider/verification_results/publish_all.rb +50 -0
  87. data/lib/pact/provider/verification_results/verification_result.rb +40 -0
  88. data/lib/pact/provider/world.rb +50 -0
  89. data/lib/pact/provider.rb +3 -0
  90. data/lib/pact/retry.rb +37 -0
  91. data/lib/pact/tasks/task_helper.rb +62 -0
  92. data/lib/pact/tasks/verification_task.rb +95 -0
  93. data/lib/pact/tasks.rb +2 -0
  94. data/lib/pact/templates/help.erb +22 -0
  95. data/lib/pact/templates/provider_state.erb +14 -0
  96. data/lib/pact/utils/metrics.rb +100 -0
  97. data/lib/pact/utils/string.rb +35 -0
  98. data/lib/pact/v2/configuration.rb +23 -0
  99. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +187 -0
  100. data/lib/pact/v2/consumer/http_interaction_builder.rb +163 -0
  101. data/lib/pact/v2/consumer/interaction_contents.rb +54 -0
  102. data/lib/pact/v2/consumer/message_interaction_builder.rb +280 -0
  103. data/lib/pact/v2/consumer/mock_server.rb +99 -0
  104. data/lib/pact/v2/consumer/pact_config/base.rb +24 -0
  105. data/lib/pact/v2/consumer/pact_config/grpc.rb +26 -0
  106. data/lib/pact/v2/consumer/pact_config/http.rb +55 -0
  107. data/lib/pact/v2/consumer/pact_config/message.rb +17 -0
  108. data/lib/pact/v2/consumer/pact_config.rb +24 -0
  109. data/lib/pact/v2/consumer.rb +8 -0
  110. data/lib/pact/v2/matchers/base.rb +67 -0
  111. data/lib/pact/v2/matchers/v1/equality.rb +19 -0
  112. data/lib/pact/v2/matchers/v2/regex.rb +19 -0
  113. data/lib/pact/v2/matchers/v2/type.rb +17 -0
  114. data/lib/pact/v2/matchers/v3/boolean.rb +17 -0
  115. data/lib/pact/v2/matchers/v3/date.rb +18 -0
  116. data/lib/pact/v2/matchers/v3/date_time.rb +18 -0
  117. data/lib/pact/v2/matchers/v3/decimal.rb +17 -0
  118. data/lib/pact/v2/matchers/v3/each.rb +42 -0
  119. data/lib/pact/v2/matchers/v3/include.rb +17 -0
  120. data/lib/pact/v2/matchers/v3/integer.rb +17 -0
  121. data/lib/pact/v2/matchers/v3/number.rb +17 -0
  122. data/lib/pact/v2/matchers/v3/time.rb +18 -0
  123. data/lib/pact/v2/matchers/v4/each_key.rb +26 -0
  124. data/lib/pact/v2/matchers/v4/each_key_value.rb +32 -0
  125. data/lib/pact/v2/matchers/v4/each_value.rb +33 -0
  126. data/lib/pact/v2/matchers/v4/not_empty.rb +17 -0
  127. data/lib/pact/v2/matchers.rb +94 -0
  128. data/lib/pact/v2/native/blocking_verifier.rb +17 -0
  129. data/lib/pact/v2/native/logger.rb +25 -0
  130. data/lib/pact/v2/provider/async_message_verifier.rb +28 -0
  131. data/lib/pact/v2/provider/base_verifier.rb +242 -0
  132. data/lib/pact/v2/provider/grpc_verifier.rb +38 -0
  133. data/lib/pact/v2/provider/gruf_server.rb +75 -0
  134. data/lib/pact/v2/provider/http_server.rb +79 -0
  135. data/lib/pact/v2/provider/http_verifier.rb +43 -0
  136. data/lib/pact/v2/provider/message_provider_servlet.rb +79 -0
  137. data/lib/pact/v2/provider/mixed_verifier.rb +22 -0
  138. data/lib/pact/v2/provider/pact_broker_proxy.rb +71 -0
  139. data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +77 -0
  140. data/lib/pact/v2/provider/pact_config/async.rb +29 -0
  141. data/lib/pact/v2/provider/pact_config/base.rb +101 -0
  142. data/lib/pact/v2/provider/pact_config/grpc.rb +26 -0
  143. data/lib/pact/v2/provider/pact_config/http.rb +27 -0
  144. data/lib/pact/v2/provider/pact_config/mixed.rb +39 -0
  145. data/lib/pact/v2/provider/pact_config.rb +26 -0
  146. data/lib/pact/v2/provider/provider_server_runner.rb +89 -0
  147. data/lib/pact/v2/provider/provider_state_configuration.rb +32 -0
  148. data/lib/pact/v2/provider/provider_state_servlet.rb +86 -0
  149. data/lib/pact/v2/provider.rb +8 -0
  150. data/lib/pact/v2/railtie.rb +13 -0
  151. data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +80 -0
  152. data/lib/pact/v2/rspec/support/pact_message_helpers.rb +42 -0
  153. data/lib/pact/v2/rspec/support/pact_provider_helpers.rb +129 -0
  154. data/lib/pact/v2/rspec/support/waterdrop/pact_waterdrop_client.rb +27 -0
  155. data/lib/pact/v2/rspec/support/webmock/webmock_helpers.rb +30 -0
  156. data/lib/pact/v2/rspec.rb +17 -0
  157. data/lib/pact/v2/tasks/pact.rake +13 -0
  158. data/lib/pact/v2/version.rb +8 -0
  159. data/lib/pact/v2.rb +71 -0
  160. data/lib/pact/version.rb +4 -0
  161. data/lib/pact.rb +13 -0
  162. data/lib/tasks/pact.rake +34 -0
  163. data/pact.gemspec +106 -0
  164. metadata +529 -0
@@ -0,0 +1,110 @@
1
+ require 'uri'
2
+ require 'delegate'
3
+ require 'pact/hal/link'
4
+ require 'pact/errors'
5
+
6
+ module Pact
7
+ module Hal
8
+ class RelationNotFoundError < ::Pact::Error; end
9
+
10
+ class ErrorResponseReturned < ::Pact::Error; end
11
+
12
+ class Entity
13
+
14
+ def initialize(href, data, http_client, response = nil)
15
+ @href = href
16
+ @data = data
17
+ @links = (@data || {}).fetch("_links", {})
18
+ @client = http_client
19
+ @response = response
20
+ end
21
+
22
+ def get(key, *args)
23
+ _link(key).get(*args)
24
+ end
25
+
26
+ def get!(key, *args)
27
+ _link(key).get!(*args)
28
+ end
29
+
30
+ def post(key, *args)
31
+ _link(key).post(*args)
32
+ end
33
+
34
+ def put(key, *args)
35
+ _link(key).put(*args)
36
+ end
37
+
38
+ def can?(key)
39
+ @links.key? key.to_s
40
+ end
41
+
42
+ def follow(key, http_method, *args)
43
+ Link.new(@links[key].merge(method: http_method), @client).run(*args)
44
+ end
45
+
46
+ def _link(key, fallback_key = nil)
47
+ if @links[key]
48
+ Link.new(@links[key], @client)
49
+ elsif fallback_key && @links[fallback_key]
50
+ Link.new(@links[fallback_key], @client)
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ def _link!(key)
57
+ _link(key) or raise RelationNotFoundError.new("Could not find relation '#{key}' in resource at #{@href}")
58
+ end
59
+
60
+ def success?
61
+ true
62
+ end
63
+
64
+ def response
65
+ @response
66
+ end
67
+
68
+ def fetch(key, fallback_key = nil)
69
+ @links[key] || (fallback_key && @links[fallback_key])
70
+ end
71
+
72
+ def method_missing(method_name, *args, &block)
73
+ if @data.key?(method_name.to_s)
74
+ @data[method_name.to_s]
75
+ elsif @links.key?(method_name)
76
+ Link.new(@links[method_name], @client).run(*args)
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ def respond_to_missing?(method_name, include_private = false)
83
+ @data.key?(method_name) || @links.key?(method_name)
84
+ end
85
+
86
+ def assert_success!
87
+ self
88
+ end
89
+ end
90
+
91
+ class ErrorEntity < Entity
92
+
93
+ def initialize(href, data, http_client, response = nil)
94
+ @href = href
95
+ @data = data
96
+ @links = {}
97
+ @client = http_client
98
+ @response = response
99
+ end
100
+
101
+ def success?
102
+ false
103
+ end
104
+
105
+ def assert_success!
106
+ raise ErrorResponseReturned.new("Error retrieving #{@href} status=#{response ? response.code: nil} #{response ? response.raw_body : ''}")
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,128 @@
1
+ require 'pact/retry'
2
+ require 'pact/hal/authorization_header_redactor'
3
+ require 'net/http'
4
+ require 'rack'
5
+ require 'openssl'
6
+
7
+ module Pact
8
+ module Hal
9
+ class HttpClient
10
+ attr_accessor :username, :password, :verbose, :token
11
+
12
+ def initialize options
13
+ @username = options[:username]
14
+ @password = options[:password]
15
+ @verbose = options[:verbose]
16
+ @token = options[:token]
17
+ end
18
+
19
+ def get href, params = {}, headers = {}
20
+ uri = URI(href)
21
+ if params && params.any?
22
+ existing_params = Rack::Utils.parse_nested_query(uri.query)
23
+ uri.query = Rack::Utils.build_nested_query(existing_params.merge(params))
24
+ end
25
+ perform_request(create_request(uri, 'Get', nil, headers), uri)
26
+ end
27
+
28
+ def put href, body = nil, headers = {}
29
+ uri = URI(href)
30
+ perform_request(create_request(uri, 'Put', body, headers), uri)
31
+ end
32
+
33
+ def post href, body = nil, headers = {}
34
+ uri = URI(href)
35
+ perform_request(create_request(uri, 'Post', body, headers), uri)
36
+ end
37
+
38
+ def create_request uri, http_method, body = nil, headers = {}
39
+ request = Net::HTTP.const_get(http_method).new(uri.request_uri)
40
+ headers.each do | key, value |
41
+ request[key] = value
42
+ end
43
+ request.body = body if body
44
+ request.basic_auth username, password if username
45
+ request['Authorization'] = "Bearer #{token}" if token
46
+ request
47
+ end
48
+
49
+ def perform_request request, uri
50
+ response = Retry.until_true do
51
+ http = Net::HTTP.new(uri.host, uri.port, :ENV)
52
+ http.set_debug_output(output_stream) if verbose?
53
+ http.use_ssl = (uri.scheme == 'https')
54
+ http.ca_file = ENV['SSL_CERT_FILE'] if ENV['SSL_CERT_FILE'] && ENV['SSL_CERT_FILE'] != ''
55
+ http.ca_path = ENV['SSL_CERT_DIR'] if ENV['SSL_CERT_DIR'] && ENV['SSL_CERT_DIR'] != ''
56
+
57
+ if x509_certificate?
58
+ http.cert = OpenSSL::X509::Certificate.new(x509_client_cert_file)
59
+ http.key = OpenSSL::PKey::RSA.new(x509_client_key_file)
60
+ end
61
+
62
+ if disable_ssl_verification?
63
+ if verbose?
64
+ Pact.configuration.output_stream.puts("SSL verification is disabled")
65
+ end
66
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
67
+ end
68
+ http.start do |http|
69
+ http.request request
70
+ end
71
+ end
72
+ Response.new(response)
73
+ end
74
+
75
+ def output_stream
76
+ AuthorizationHeaderRedactor.new(Pact.configuration.output_stream)
77
+ end
78
+
79
+ def verbose?
80
+ verbose || ENV['VERBOSE'] == 'true'
81
+ end
82
+
83
+ def x509_certificate?
84
+ ENV['X509_CLIENT_CERT_FILE'] && ENV['X509_CLIENT_CERT_FILE'] != '' &&
85
+ ENV['X509_CLIENT_KEY_FILE'] && ENV['X509_CLIENT_KEY_FILE'] != ''
86
+ end
87
+
88
+ def x509_client_cert_file
89
+ File.read(ENV['X509_CLIENT_CERT_FILE'])
90
+ end
91
+
92
+ def x509_client_key_file
93
+ File.read(ENV['X509_CLIENT_KEY_FILE'])
94
+ end
95
+
96
+ def disable_ssl_verification?
97
+ ENV['PACT_DISABLE_SSL_VERIFICATION'] == 'true' || ENV['PACT_BROKER_DISABLE_SSL_VERIFICATION'] == 'true'
98
+ end
99
+
100
+ class Response < SimpleDelegator
101
+ def body
102
+ bod = raw_body
103
+ if bod && bod != ''
104
+ JSON.parse(bod)
105
+ else
106
+ nil
107
+ end
108
+ end
109
+
110
+ def raw_body
111
+ __getobj__().body
112
+ end
113
+
114
+ def status
115
+ code.to_i
116
+ end
117
+
118
+ def success?
119
+ __getobj__().code.start_with?("2")
120
+ end
121
+
122
+ def json?
123
+ self['content-type'] && self['content-type'] =~ /json/
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,112 @@
1
+ require 'erb'
2
+ require 'delegate'
3
+
4
+ module Pact
5
+ module Hal
6
+ class Link
7
+ attr_reader :request_method, :href
8
+
9
+ DEFAULT_GET_HEADERS = {
10
+ "Accept" => "application/hal+json"
11
+ }.freeze
12
+
13
+ DEFAULT_POST_HEADERS = {
14
+ "Accept" => "application/hal+json",
15
+ "Content-Type" => "application/json"
16
+ }.freeze
17
+
18
+ def initialize(attrs, http_client)
19
+ @attrs = attrs
20
+ @request_method = attrs.fetch(:method, :get).to_sym
21
+ @href = attrs.fetch('href')
22
+ @http_client = http_client
23
+ end
24
+
25
+ def run(payload = nil)
26
+ case request_method
27
+ when :get
28
+ get(payload)
29
+ when :put
30
+ put(payload)
31
+ when :post
32
+ post(payload)
33
+ end
34
+ end
35
+
36
+ def title_or_name
37
+ title || name
38
+ end
39
+
40
+ def title
41
+ @attrs['title']
42
+ end
43
+
44
+ def name
45
+ @attrs['name']
46
+ end
47
+
48
+ def get(payload = {}, headers = {})
49
+ wrap_response(href, @http_client.get(href, payload, DEFAULT_GET_HEADERS.merge(headers)))
50
+ end
51
+
52
+ def get!(*args)
53
+ get(*args).assert_success!
54
+ end
55
+
56
+ def put(payload = nil, headers = {})
57
+ wrap_response(href, @http_client.put(href, payload ? payload.to_json : nil, DEFAULT_POST_HEADERS.merge(headers)))
58
+ end
59
+
60
+ def post(payload = nil, headers = {})
61
+ wrap_response(href, @http_client.post(href, payload ? payload.to_json : nil, DEFAULT_POST_HEADERS.merge(headers)))
62
+ end
63
+
64
+ def post!(payload = nil, headers = {})
65
+ post(payload, headers).assert_success!
66
+ end
67
+
68
+ def expand(params)
69
+ expanded_url = expand_url(params, href)
70
+ new_attrs = @attrs.merge('href' => expanded_url)
71
+ Link.new(new_attrs, http_client)
72
+ end
73
+
74
+ def with_query(query)
75
+ if query && query.any?
76
+ uri = URI(href)
77
+ existing_query_params = Rack::Utils.parse_nested_query(uri.query)
78
+ uri.query = Rack::Utils.build_nested_query(existing_query_params.merge(query))
79
+ new_attrs = attrs.merge('href' => uri.to_s)
80
+ Link.new(new_attrs, http_client)
81
+ else
82
+ self
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ attr_reader :attrs, :http_client
89
+
90
+ def wrap_response(href, http_response)
91
+ require 'pact/hal/entity' # avoid circular reference
92
+ require 'pact/hal/non_json_entity'
93
+
94
+ if http_response.success?
95
+ if http_response.json?
96
+ Entity.new(href, http_response.body, @http_client, http_response)
97
+ else
98
+ NonJsonEntity.new(href, http_response.raw_body, @http_client, http_response)
99
+ end
100
+ else
101
+ ErrorEntity.new(href, http_response.raw_body, @http_client, http_response)
102
+ end
103
+ end
104
+
105
+ def expand_url(params, url)
106
+ params.inject(url) do | url, (key, value) |
107
+ url.gsub('{' + key.to_s + '}', ERB::Util.url_encode(value))
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,28 @@
1
+ module Pact
2
+ module Hal
3
+ class NonJsonEntity
4
+ def initialize(href, body, http_client, response = nil)
5
+ @href = href
6
+ @body = body
7
+ @client = http_client
8
+ @response = response
9
+ end
10
+
11
+ def success?
12
+ true
13
+ end
14
+
15
+ def response
16
+ @response
17
+ end
18
+
19
+ def body
20
+ @body
21
+ end
22
+
23
+ def assert_success!
24
+ self
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Pact
2
+ module HashRefinements
3
+ refine Hash do
4
+ def compact
5
+ h = {}
6
+ each do |key, value|
7
+ h[key] = value unless value == nil
8
+ end
9
+ h
10
+ end unless Hash.method_defined? :compact
11
+
12
+ def compact!
13
+ reject! {|_key, value| value == nil}
14
+ end unless Hash.method_defined? :compact!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,112 @@
1
+ require 'pact/hal/entity'
2
+ require 'pact/hal/http_client'
3
+ require 'pact/provider/pact_uri'
4
+ require 'pact/errors'
5
+ require 'pact/pact_broker/fetch_pacts'
6
+ require 'pact/pact_broker/notices'
7
+ require 'pact/pact_broker/pact_selection_description'
8
+ require "pact/hash_refinements"
9
+
10
+ module Pact
11
+ module PactBroker
12
+ class FetchPactURIsForVerification
13
+ using Pact::HashRefinements
14
+
15
+ include PactSelectionDescription
16
+ attr_reader :provider, :consumer_version_selectors, :provider_version_branch, :provider_version_tags, :broker_base_url, :http_client_options, :http_client, :options
17
+
18
+ PACTS_FOR_VERIFICATION_RELATION = 'pb:provider-pacts-for-verification'.freeze
19
+ PACTS_FOR_VERIFICATION_RELATION_BETA = 'beta:provider-pacts-for-verification'.freeze
20
+ PACTS = 'pacts'.freeze
21
+ HREF = 'href'.freeze
22
+ LINKS = '_links'.freeze
23
+ SELF = 'self'.freeze
24
+ EMBEDDED = '_embedded'.freeze
25
+
26
+ def initialize(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {})
27
+ @provider = provider
28
+ @consumer_version_selectors = consumer_version_selectors || []
29
+ @provider_version_branch = provider_version_branch
30
+ @provider_version_tags = [*provider_version_tags]
31
+ @http_client_options = http_client_options
32
+ @broker_base_url = broker_base_url
33
+ @http_client = Pact::Hal::HttpClient.new(http_client_options)
34
+ @options = options
35
+ end
36
+
37
+ def self.call(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options = {})
38
+ new(provider, consumer_version_selectors, provider_version_branch, provider_version_tags, broker_base_url, http_client_options, options).call
39
+ end
40
+
41
+ def call
42
+ handling_no_pacts_found do
43
+ if index.can?(PACTS_FOR_VERIFICATION_RELATION) || index.can?(PACTS_FOR_VERIFICATION_RELATION_BETA)
44
+ log_message
45
+ pacts_for_verification
46
+ else
47
+ old_selectors = consumer_version_selectors.collect do | selector |
48
+ { name: selector[:tag], all: !selector[:latest], fallback: selector[:fallbackTag]}
49
+ end
50
+ # Fall back to old method of fetching pacts
51
+ FetchPacts.call(provider, old_selectors, broker_base_url, http_client_options)
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def index
59
+ @index_entity ||= Pact::Hal::Link.new({ "href" => broker_base_url }, http_client).get.assert_success!
60
+ end
61
+
62
+ def pacts_for_verification
63
+ pacts_for_verification_entity.response.body[EMBEDDED][PACTS].collect do | pact |
64
+ metadata = {
65
+ pending: pact["verificationProperties"]["pending"],
66
+ notices: extract_notices(pact),
67
+ short_description: pact["shortDescription"]
68
+ }
69
+ Pact::Provider::PactURI.new(pact[LINKS][SELF][HREF], http_client_options, metadata)
70
+ end
71
+ end
72
+
73
+ def pacts_for_verification_entity
74
+ index
75
+ ._link(PACTS_FOR_VERIFICATION_RELATION, PACTS_FOR_VERIFICATION_RELATION_BETA)
76
+ .expand(provider: provider)
77
+ .post!(query)
78
+ end
79
+
80
+ def query
81
+ q = {}
82
+ q["includePendingStatus"] = options[:include_pending_status]
83
+ q["consumerVersionSelectors"] = consumer_version_selectors if consumer_version_selectors.any?
84
+ q["providerVersionTags"] = provider_version_tags if provider_version_tags.any?
85
+ q["providerVersionBranch"] = provider_version_branch
86
+ q["includeWipPactsSince"] = options[:include_wip_pacts_since]
87
+ q.compact
88
+ end
89
+
90
+ def extract_notices(pact)
91
+ Notices.new((pact["verificationProperties"]["notices"] || []).collect{ |notice| symbolize_keys(notice) })
92
+ end
93
+
94
+ def symbolize_keys(hash)
95
+ hash.each_with_object({}){ |(k,v), h| h[k.to_sym] = v }
96
+ end
97
+
98
+ def log_message
99
+ Pact.configuration.output_stream.puts "INFO: #{pact_selection_description(provider, consumer_version_selectors, options, broker_base_url)}"
100
+ end
101
+
102
+ def handling_no_pacts_found
103
+ pacts_found = yield
104
+ raise "No pacts found to verify" if pacts_found.empty? && options[:fail_if_no_pacts_found] != false
105
+ if pacts_found.empty? && options[:fail_if_no_pacts_found] == false
106
+ Pact.configuration.output_stream.puts "WARN: No pacts found to verify & fail_if_no_pacts_found is set to false."
107
+ end
108
+ pacts_found
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,103 @@
1
+ require 'pact/hal/entity'
2
+ require 'pact/hal/http_client'
3
+ require 'pact/provider/pact_uri'
4
+ require 'pact/errors'
5
+
6
+ module Pact
7
+ module PactBroker
8
+ class FetchPacts
9
+ attr_reader :provider, :tags, :broker_base_url, :http_client_options, :http_client, :index_entity
10
+
11
+ ALL_PROVIDER_TAG_RELATION = 'pb:provider-pacts-with-tag'.freeze
12
+ LATEST_PROVIDER_TAG_RELATION = 'pb:latest-provider-pacts-with-tag'.freeze
13
+ LATEST_PROVIDER_RELATION = 'pb:latest-provider-pacts'.freeze
14
+ PACTS = 'pacts'.freeze
15
+ PB_PACTS = 'pb:pacts'.freeze
16
+ HREF = 'href'.freeze
17
+
18
+ def initialize(provider, tags, broker_base_url, http_client_options)
19
+ @provider = provider
20
+ @tags = (tags || []).collect do |tag|
21
+ if tag.is_a?(String)
22
+ { name: tag, all: false, fallback: nil }
23
+ else
24
+ tag
25
+ end
26
+ end
27
+ @http_client_options = http_client_options
28
+ @broker_base_url = broker_base_url
29
+ @http_client = Pact::Hal::HttpClient.new(http_client_options)
30
+ end
31
+
32
+ def self.call(provider, tags, broker_base_url, http_client_options)
33
+ new(provider, tags, broker_base_url, http_client_options).call
34
+ end
35
+
36
+ def call
37
+ log_message
38
+ if index.success?
39
+ if any_tags?
40
+ tagged_pacts_for_provider
41
+ else
42
+ latest_pacts_for_provider
43
+ end
44
+ else
45
+ raise Pact::Error.new("Error retrieving #{broker_base_url} status=#{index_entity.response.code} #{index_entity.response.raw_body}")
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def any_tags?
52
+ tags && tags.any?
53
+ end
54
+
55
+ def tagged_pacts_for_provider
56
+ tags.collect do |tag|
57
+ link = link_for(tag)
58
+ urls = pact_urls(link.expand(provider: provider, tag: tag[:name]).get)
59
+ if urls.empty? && tag[:fallback]
60
+ urls = pact_urls(link.expand(provider: provider, tag: tag[:fallback]).get)
61
+ end
62
+ urls
63
+ end.flatten
64
+ end
65
+
66
+ def link_for(tag)
67
+ if !tag[:all]
68
+ index_entity._link!(LATEST_PROVIDER_TAG_RELATION)
69
+ else
70
+ index_entity._link!(ALL_PROVIDER_TAG_RELATION)
71
+ end
72
+ end
73
+
74
+ def index
75
+ @index_entity ||= Pact::Hal::Link.new({ "href" => broker_base_url }, http_client).get.assert_success!
76
+ end
77
+
78
+ def latest_pacts_for_provider
79
+ link = index_entity._link!(LATEST_PROVIDER_RELATION)
80
+ pact_urls(link.expand(provider: provider).get.assert_success!)
81
+ end
82
+
83
+ def pact_urls(link_by_provider)
84
+ link_by_provider.assert_success!.fetch(PB_PACTS, PACTS).collect do |pact|
85
+ Pact::Provider::PactURI.new(pact[HREF], http_client_options)
86
+ end
87
+ end
88
+
89
+ def log_message
90
+ message = "INFO: Fetching pacts for #{provider} from #{broker_base_url}"
91
+ if tags.any?
92
+ desc = tags.collect do |tag|
93
+ all_or_latest = tag[:all] ? "all" : "latest"
94
+ name = tag[:fallback] ? "#{tag[:name]} (or #{tag[:fallback]} if not found)" : tag[:name]
95
+ "#{all_or_latest} #{name}"
96
+ end.join(", ")
97
+ message << " for tags: #{desc}"
98
+ end
99
+ Pact.configuration.output_stream.puts message
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,34 @@
1
+ module Pact
2
+ module PactBroker
3
+ class Notices < Array
4
+ def before_verification_notices
5
+ select { | notice | notice[:when].nil? || notice[:when].start_with?('before_verification') }
6
+ end
7
+
8
+ def before_verification_notices_text
9
+ before_verification_notices.collect{ | notice | notice[:text] }
10
+ end
11
+
12
+ def after_verification_notices(success, published)
13
+ select { | notice | notice[:when] == "after_verification:success_#{success}_published_#{published}" || notice[:when] == "after_verification" }
14
+ .collect do | notice |
15
+ notice.merge(:when => simplify_notice_when(notice[:when]))
16
+ end
17
+ end
18
+
19
+ def after_verification_notices_text(success, published)
20
+ after_verification_notices(success, published).collect{ | notice | notice[:text] }
21
+ end
22
+
23
+ def all_notices(success, published)
24
+ before_verification_notices + after_verification_notices(success, published)
25
+ end
26
+
27
+ private
28
+
29
+ def simplify_notice_when(when_key)
30
+ when_key.split(":").first
31
+ end
32
+ end
33
+ end
34
+ end