pact 1.67.5 → 2.0.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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/lib/pact/configuration.rb +21 -0
  4. data/lib/pact/consumer/grpc_interaction_builder.rb +192 -0
  5. data/lib/pact/consumer/http_interaction_builder.rb +160 -0
  6. data/lib/pact/consumer/interaction_contents.rb +62 -0
  7. data/lib/pact/consumer/message_interaction_builder.rb +286 -0
  8. data/lib/pact/consumer/mock_server.rb +106 -0
  9. data/lib/pact/consumer/pact_config/base.rb +22 -0
  10. data/lib/pact/consumer/pact_config/grpc.rb +24 -0
  11. data/lib/pact/consumer/pact_config/http.rb +53 -0
  12. data/lib/pact/consumer/pact_config/message.rb +15 -0
  13. data/lib/pact/consumer/pact_config/plugin_async_message.rb +24 -0
  14. data/lib/pact/consumer/pact_config/plugin_http.rb +24 -0
  15. data/lib/pact/consumer/pact_config/plugin_sync_message.rb +24 -0
  16. data/lib/pact/consumer/pact_config.rb +28 -0
  17. data/lib/pact/consumer/plugin_async_message_interaction_builder.rb +169 -0
  18. data/lib/pact/consumer/plugin_http_interaction_builder.rb +199 -0
  19. data/lib/pact/consumer/plugin_sync_message_interaction_builder.rb +178 -0
  20. data/lib/pact/consumer.rb +6 -7
  21. data/lib/pact/generators/base.rb +285 -0
  22. data/lib/pact/generators.rb +47 -0
  23. data/lib/pact/matchers/base.rb +72 -0
  24. data/lib/pact/matchers/v1/equality.rb +17 -0
  25. data/lib/pact/matchers/v2/regex.rb +26 -0
  26. data/lib/pact/matchers/v2/type.rb +18 -0
  27. data/lib/pact/matchers/v3/boolean.rb +18 -0
  28. data/lib/pact/matchers/v3/content_type.rb +30 -0
  29. data/lib/pact/matchers/v3/date.rb +22 -0
  30. data/lib/pact/matchers/v3/date_time.rb +22 -0
  31. data/lib/pact/matchers/v3/decimal.rb +18 -0
  32. data/lib/pact/matchers/v3/each.rb +46 -0
  33. data/lib/pact/matchers/v3/include.rb +18 -0
  34. data/lib/pact/matchers/v3/integer.rb +18 -0
  35. data/lib/pact/matchers/v3/null.rb +13 -0
  36. data/lib/pact/matchers/v3/number.rb +18 -0
  37. data/lib/pact/matchers/v3/semver.rb +22 -0
  38. data/lib/pact/matchers/v3/time.rb +22 -0
  39. data/lib/pact/matchers/v3/values.rb +13 -0
  40. data/lib/pact/matchers/v4/each_key.rb +34 -0
  41. data/lib/pact/matchers/v4/each_key_value.rb +38 -0
  42. data/lib/pact/matchers/v4/each_value.rb +40 -0
  43. data/lib/pact/matchers/v4/not_empty.rb +22 -0
  44. data/lib/pact/matchers/v4/status_code.rb +15 -0
  45. data/lib/pact/matchers.rb +108 -0
  46. data/lib/pact/native/blocking_verifier.rb +15 -0
  47. data/lib/pact/native/logger.rb +24 -0
  48. data/lib/pact/provider/async_message_verifier.rb +29 -0
  49. data/lib/pact/provider/base_verifier.rb +259 -0
  50. data/lib/pact/provider/grpc_verifier.rb +40 -0
  51. data/lib/pact/provider/gruf_server.rb +73 -0
  52. data/lib/pact/provider/http_server.rb +77 -0
  53. data/lib/pact/provider/http_verifier.rb +45 -0
  54. data/lib/pact/provider/message_provider_servlet.rb +77 -0
  55. data/lib/pact/provider/mixed_verifier.rb +21 -0
  56. data/lib/pact/provider/pact_broker_proxy.rb +62 -0
  57. data/lib/pact/provider/pact_broker_proxy_runner.rb +78 -0
  58. data/lib/pact/provider/pact_config/async.rb +28 -0
  59. data/lib/pact/provider/pact_config/base.rb +101 -0
  60. data/lib/pact/provider/pact_config/grpc.rb +24 -0
  61. data/lib/pact/provider/pact_config/http.rb +25 -0
  62. data/lib/pact/provider/pact_config/mixed.rb +38 -0
  63. data/lib/pact/provider/pact_config.rb +24 -0
  64. data/lib/pact/provider/provider_server_runner.rb +90 -0
  65. data/lib/pact/provider/provider_state_configuration.rb +32 -0
  66. data/lib/pact/provider/provider_state_servlet.rb +84 -0
  67. data/lib/pact/provider.rb +6 -3
  68. data/lib/pact/railtie.rb +11 -0
  69. data/lib/pact/{v2/rspec → rspec}/support/pact_consumer_helpers.rb +8 -8
  70. data/lib/pact/{v2/rspec → rspec}/support/pact_provider_helpers.rb +25 -24
  71. data/lib/pact/{v2/rspec → rspec}/support/webmock/webmock_helpers.rb +1 -1
  72. data/lib/pact/{v2/rspec.rb → rspec.rb} +6 -5
  73. data/lib/pact/support/core_ext.rb +93 -0
  74. data/lib/pact/tasks/pact.rake +13 -0
  75. data/lib/pact/version.rb +1 -1
  76. data/lib/pact.rb +52 -11
  77. data/pact.gemspec +47 -64
  78. metadata +146 -404
  79. data/lib/pact/cli/generate_pact_docs.rb +0 -4
  80. data/lib/pact/cli/run_pact_verification.rb +0 -99
  81. data/lib/pact/cli/spec_criteria.rb +0 -26
  82. data/lib/pact/cli.rb +0 -45
  83. data/lib/pact/consumer/configuration/configuration_extensions.rb +0 -90
  84. data/lib/pact/consumer/configuration/dsl.rb +0 -11
  85. data/lib/pact/consumer/configuration/mock_service.rb +0 -112
  86. data/lib/pact/consumer/configuration/service_consumer.rb +0 -51
  87. data/lib/pact/consumer/configuration/service_provider.rb +0 -40
  88. data/lib/pact/consumer/configuration.rb +0 -10
  89. data/lib/pact/consumer/consumer_contract_builder.rb +0 -82
  90. data/lib/pact/consumer/consumer_contract_builders.rb +0 -10
  91. data/lib/pact/consumer/interaction_builder.rb +0 -45
  92. data/lib/pact/consumer/rspec.rb +0 -35
  93. data/lib/pact/consumer/spec_hooks.rb +0 -40
  94. data/lib/pact/consumer/world.rb +0 -37
  95. data/lib/pact/doc/README.md +0 -13
  96. data/lib/pact/doc/doc_file.rb +0 -40
  97. data/lib/pact/doc/generate.rb +0 -11
  98. data/lib/pact/doc/generator.rb +0 -82
  99. data/lib/pact/doc/interaction_view_model.rb +0 -124
  100. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +0 -68
  101. data/lib/pact/doc/markdown/generator.rb +0 -26
  102. data/lib/pact/doc/markdown/index_renderer.rb +0 -43
  103. data/lib/pact/doc/markdown/interaction.erb +0 -14
  104. data/lib/pact/doc/markdown/interaction_renderer.rb +0 -43
  105. data/lib/pact/doc/sort_interactions.rb +0 -16
  106. data/lib/pact/hal/authorization_header_redactor.rb +0 -32
  107. data/lib/pact/hal/entity.rb +0 -110
  108. data/lib/pact/hal/http_client.rb +0 -146
  109. data/lib/pact/hal/link.rb +0 -112
  110. data/lib/pact/hal/non_json_entity.rb +0 -28
  111. data/lib/pact/hash_refinements.rb +0 -17
  112. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +0 -112
  113. data/lib/pact/pact_broker/fetch_pacts.rb +0 -103
  114. data/lib/pact/pact_broker/notices.rb +0 -34
  115. data/lib/pact/pact_broker/pact_selection_description.rb +0 -66
  116. data/lib/pact/pact_broker.rb +0 -25
  117. data/lib/pact/project_root.rb +0 -7
  118. data/lib/pact/provider/configuration/configuration_extension.rb +0 -69
  119. data/lib/pact/provider/configuration/dsl.rb +0 -18
  120. data/lib/pact/provider/configuration/message_provider_dsl.rb +0 -63
  121. data/lib/pact/provider/configuration/pact_verification.rb +0 -48
  122. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +0 -126
  123. data/lib/pact/provider/configuration/service_provider_config.rb +0 -32
  124. data/lib/pact/provider/configuration/service_provider_dsl.rb +0 -107
  125. data/lib/pact/provider/configuration.rb +0 -7
  126. data/lib/pact/provider/context.rb +0 -0
  127. data/lib/pact/provider/help/console_text.rb +0 -76
  128. data/lib/pact/provider/help/content.rb +0 -38
  129. data/lib/pact/provider/help/pact_diff.rb +0 -43
  130. data/lib/pact/provider/help/prompt_text.rb +0 -49
  131. data/lib/pact/provider/help/write.rb +0 -56
  132. data/lib/pact/provider/matchers/messages.rb +0 -66
  133. data/lib/pact/provider/pact_helper_locator.rb +0 -24
  134. data/lib/pact/provider/pact_source.rb +0 -40
  135. data/lib/pact/provider/pact_spec_runner.rb +0 -188
  136. data/lib/pact/provider/pact_uri.rb +0 -55
  137. data/lib/pact/provider/pact_verification.rb +0 -17
  138. data/lib/pact/provider/print_missing_provider_states.rb +0 -35
  139. data/lib/pact/provider/request.rb +0 -90
  140. data/lib/pact/provider/rspec/backtrace_formatter.rb +0 -43
  141. data/lib/pact/provider/rspec/calculate_exit_code.rb +0 -18
  142. data/lib/pact/provider/rspec/custom_options_file +0 -0
  143. data/lib/pact/provider/rspec/formatter_rspec_2.rb +0 -76
  144. data/lib/pact/provider/rspec/formatter_rspec_3.rb +0 -195
  145. data/lib/pact/provider/rspec/json_formatter.rb +0 -100
  146. data/lib/pact/provider/rspec/matchers.rb +0 -80
  147. data/lib/pact/provider/rspec/pact_broker_formatter.rb +0 -76
  148. data/lib/pact/provider/rspec.rb +0 -234
  149. data/lib/pact/provider/state/provider_state.rb +0 -180
  150. data/lib/pact/provider/state/provider_state_configured_modules.rb +0 -15
  151. data/lib/pact/provider/state/provider_state_manager.rb +0 -42
  152. data/lib/pact/provider/state/provider_state_proxy.rb +0 -39
  153. data/lib/pact/provider/state/set_up.rb +0 -13
  154. data/lib/pact/provider/state/tear_down.rb +0 -13
  155. data/lib/pact/provider/test_methods.rb +0 -77
  156. data/lib/pact/provider/verification_report.rb +0 -36
  157. data/lib/pact/provider/verification_results/create.rb +0 -88
  158. data/lib/pact/provider/verification_results/publish.rb +0 -143
  159. data/lib/pact/provider/verification_results/publish_all.rb +0 -50
  160. data/lib/pact/provider/verification_results/verification_result.rb +0 -40
  161. data/lib/pact/provider/world.rb +0 -50
  162. data/lib/pact/retry.rb +0 -37
  163. data/lib/pact/tasks/task_helper.rb +0 -62
  164. data/lib/pact/tasks/verification_task.rb +0 -95
  165. data/lib/pact/tasks.rb +0 -2
  166. data/lib/pact/templates/help.erb +0 -22
  167. data/lib/pact/templates/provider_state.erb +0 -14
  168. data/lib/pact/utils/metrics.rb +0 -100
  169. data/lib/pact/utils/string.rb +0 -35
  170. data/lib/pact/v2/configuration.rb +0 -23
  171. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +0 -194
  172. data/lib/pact/v2/consumer/http_interaction_builder.rb +0 -162
  173. data/lib/pact/v2/consumer/interaction_contents.rb +0 -62
  174. data/lib/pact/v2/consumer/message_interaction_builder.rb +0 -288
  175. data/lib/pact/v2/consumer/mock_server.rb +0 -108
  176. data/lib/pact/v2/consumer/pact_config/base.rb +0 -24
  177. data/lib/pact/v2/consumer/pact_config/grpc.rb +0 -26
  178. data/lib/pact/v2/consumer/pact_config/http.rb +0 -55
  179. data/lib/pact/v2/consumer/pact_config/message.rb +0 -17
  180. data/lib/pact/v2/consumer/pact_config/plugin_async_message.rb +0 -26
  181. data/lib/pact/v2/consumer/pact_config/plugin_http.rb +0 -26
  182. data/lib/pact/v2/consumer/pact_config/plugin_sync_message.rb +0 -26
  183. data/lib/pact/v2/consumer/pact_config.rb +0 -30
  184. data/lib/pact/v2/consumer/plugin_async_message_interaction_builder.rb +0 -171
  185. data/lib/pact/v2/consumer/plugin_http_interaction_builder.rb +0 -201
  186. data/lib/pact/v2/consumer/plugin_sync_message_interaction_builder.rb +0 -180
  187. data/lib/pact/v2/consumer.rb +0 -8
  188. data/lib/pact/v2/generators/base.rb +0 -287
  189. data/lib/pact/v2/generators.rb +0 -49
  190. data/lib/pact/v2/matchers/base.rb +0 -74
  191. data/lib/pact/v2/matchers/v1/equality.rb +0 -19
  192. data/lib/pact/v2/matchers/v2/regex.rb +0 -19
  193. data/lib/pact/v2/matchers/v2/type.rb +0 -17
  194. data/lib/pact/v2/matchers/v3/boolean.rb +0 -17
  195. data/lib/pact/v2/matchers/v3/content_type.rb +0 -32
  196. data/lib/pact/v2/matchers/v3/date.rb +0 -18
  197. data/lib/pact/v2/matchers/v3/date_time.rb +0 -18
  198. data/lib/pact/v2/matchers/v3/decimal.rb +0 -17
  199. data/lib/pact/v2/matchers/v3/each.rb +0 -42
  200. data/lib/pact/v2/matchers/v3/include.rb +0 -17
  201. data/lib/pact/v2/matchers/v3/integer.rb +0 -17
  202. data/lib/pact/v2/matchers/v3/null.rb +0 -16
  203. data/lib/pact/v2/matchers/v3/number.rb +0 -17
  204. data/lib/pact/v2/matchers/v3/semver.rb +0 -23
  205. data/lib/pact/v2/matchers/v3/time.rb +0 -18
  206. data/lib/pact/v2/matchers/v3/values.rb +0 -16
  207. data/lib/pact/v2/matchers/v4/each_key.rb +0 -26
  208. data/lib/pact/v2/matchers/v4/each_key_value.rb +0 -32
  209. data/lib/pact/v2/matchers/v4/each_value.rb +0 -33
  210. data/lib/pact/v2/matchers/v4/not_empty.rb +0 -24
  211. data/lib/pact/v2/matchers/v4/status_code.rb +0 -17
  212. data/lib/pact/v2/matchers.rb +0 -110
  213. data/lib/pact/v2/native/blocking_verifier.rb +0 -17
  214. data/lib/pact/v2/native/logger.rb +0 -25
  215. data/lib/pact/v2/provider/async_message_verifier.rb +0 -28
  216. data/lib/pact/v2/provider/base_verifier.rb +0 -242
  217. data/lib/pact/v2/provider/grpc_verifier.rb +0 -38
  218. data/lib/pact/v2/provider/gruf_server.rb +0 -75
  219. data/lib/pact/v2/provider/http_server.rb +0 -79
  220. data/lib/pact/v2/provider/http_verifier.rb +0 -43
  221. data/lib/pact/v2/provider/message_provider_servlet.rb +0 -79
  222. data/lib/pact/v2/provider/mixed_verifier.rb +0 -22
  223. data/lib/pact/v2/provider/pact_broker_proxy.rb +0 -66
  224. data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +0 -80
  225. data/lib/pact/v2/provider/pact_config/async.rb +0 -29
  226. data/lib/pact/v2/provider/pact_config/base.rb +0 -103
  227. data/lib/pact/v2/provider/pact_config/grpc.rb +0 -26
  228. data/lib/pact/v2/provider/pact_config/http.rb +0 -27
  229. data/lib/pact/v2/provider/pact_config/mixed.rb +0 -40
  230. data/lib/pact/v2/provider/pact_config.rb +0 -26
  231. data/lib/pact/v2/provider/provider_server_runner.rb +0 -92
  232. data/lib/pact/v2/provider/provider_state_configuration.rb +0 -32
  233. data/lib/pact/v2/provider/provider_state_servlet.rb +0 -86
  234. data/lib/pact/v2/provider.rb +0 -8
  235. data/lib/pact/v2/railtie.rb +0 -13
  236. data/lib/pact/v2/tasks/pact.rake +0 -13
  237. data/lib/pact/v2.rb +0 -71
  238. data/lib/tasks/pact.rake +0 -34
  239. /data/lib/pact/{v2/rspec → rspec}/support/pact_message_helpers.rb +0 -0
  240. /data/lib/pact/{v2/rspec → rspec}/support/waterdrop/pact_waterdrop_client.rb +0 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pact/ffi/verifier'
4
+ require 'pact/native/logger'
5
+
6
+ module Pact
7
+ module Provider
8
+ class GrpcVerifier < BaseVerifier
9
+ PROVIDER_TRANSPORT_TYPE = 'grpc'
10
+
11
+ def initialize(pact_config, mixed_config = nil)
12
+ super
13
+
14
+ unless pact_config.is_a?(::Pact::Provider::PactConfig::Grpc)
15
+ raise ArgumentError,
16
+ 'pact_config must be an instance of Pact::Provider::PactConfig::Grpc'
17
+ end
18
+
19
+ @grpc_server = GrufServer.new(host: "127.0.0.1:#{@pact_config.grpc_port}",
20
+ services: @pact_config.grpc_services, logger: @pact_config.logger)
21
+ end
22
+
23
+ private
24
+
25
+ def add_provider_transport(pact_handle)
26
+ PactFfi::Verifier.add_provider_transport(pact_handle, PROVIDER_TRANSPORT_TYPE, @pact_config.grpc_port, '', '')
27
+ end
28
+
29
+ def start_servers!
30
+ super
31
+ @grpc_server.start
32
+ end
33
+
34
+ def stop_servers
35
+ super
36
+ @grpc_server.stop
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pact
4
+ module Provider
5
+ # inspired by Gruf::Cli::Executor
6
+ class GrufServer
7
+ SERVER_STOP_TIMEOUT_SEC = 15
8
+
9
+ def initialize(options = {})
10
+ @options = options
11
+
12
+ setup!
13
+
14
+ @server_pid = nil
15
+
16
+ @services = @options[:services].is_a?(Array) ? @options[:services] : []
17
+ @logger = @options[:logger] || ::Logger.new($stdout)
18
+ end
19
+
20
+ def start
21
+ raise "server already running, stop server before starting new one" if @thread
22
+
23
+ @logger.info("[gruf] starting standalone server with options: #{@options}")
24
+
25
+ @server = Gruf::Server.new(Gruf.server_options)
26
+ @services.each { |s| @server.add_service(s) } if @services.any?
27
+ @thread = Thread.new do
28
+ @logger.debug "[gruf] starting grpc server"
29
+ @server.start!
30
+ end
31
+ @server.server.wait_till_running(10)
32
+
33
+ @logger.info("[gruf] standalone server started")
34
+ end
35
+
36
+ def stop
37
+ @logger.info("[gruf] stopping standalone server")
38
+
39
+ @server&.server&.stop
40
+ @thread&.join(SERVER_STOP_TIMEOUT_SEC)
41
+ @thread&.kill
42
+
43
+ @logger.info("[gruf] standalone server stopped")
44
+ end
45
+
46
+ ##
47
+ # Run the server
48
+ #
49
+ def run
50
+ start
51
+
52
+ yield
53
+ rescue => e
54
+ @logger.fatal("FATAL ERROR: #{e.message} #{e.backtrace.join("\n")}")
55
+ raise
56
+ ensure
57
+ stop
58
+ end
59
+
60
+ private
61
+
62
+ def setup!
63
+ Gruf.server_binding_url = @options[:host] if @options[:host]
64
+ if @options[:suppress_default_interceptors]
65
+ Gruf.interceptors.remove(Gruf::Interceptors::ActiveRecord::ConnectionReset)
66
+ Gruf.interceptors.remove(Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
67
+ end
68
+ Gruf.backtrace_on_error = true if @options[:backtrace_on_error]
69
+ Gruf.health_check_enabled = true if @options[:health_check]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pact
4
+ module Provider
5
+ # inspired by Gruf::Cli::Executor
6
+ class HttpServer
7
+ SERVER_STOP_TIMEOUT_SEC = 15
8
+
9
+ def initialize(options = {})
10
+ @options = options
11
+
12
+ @server_pid = nil
13
+
14
+ @host = @options[:host] || "localhost"
15
+ @logger = @options[:logger] || ::Logger.new($stdout)
16
+ # allow any rack based app to be passed in, otherwise
17
+ # we will load a Rails.application
18
+ # allows for backwards compat with pact-ruby v1
19
+ @app = @options[:app] || nil
20
+ end
21
+
22
+ def start
23
+ raise "server already running, stop server before starting new one" if @thread
24
+
25
+ @logger.info("[webrick] starting server with options: #{@options}")
26
+
27
+ @thread = Thread.new do
28
+ @logger.debug "[webrick] starting http server"
29
+
30
+ # TODO: load from config.ru, if not rails and no app provided?
31
+ # Rack 2/3 compatibility
32
+ begin
33
+ require 'rack/handler/webrick'
34
+ handler = ::Rack::Handler::WEBrick
35
+ rescue LoadError
36
+ require 'rackup/handler/webrick'
37
+ handler = Class.new(Rackup::Handler::WEBrick)
38
+ end
39
+ handler.run(@app || (defined?(Rails) ? Rails.application : nil),
40
+ Host: @options[:host],
41
+ Port: @options[:port],
42
+ Logger: @logger,
43
+ StartCallback: -> { @started = true }) do |server|
44
+ @server = server
45
+ end
46
+ end
47
+ sleep 0.001 until @started
48
+
49
+ @logger.info("[webrick] server started")
50
+ end
51
+
52
+ def stop
53
+ @logger.info("[webrick] stopping server")
54
+
55
+ @server&.shutdown
56
+ @thread&.join(SERVER_STOP_TIMEOUT_SEC)
57
+ @thread&.kill
58
+
59
+ @logger.info("[webrick] server stopped")
60
+ end
61
+
62
+ ##
63
+ # Run the server
64
+ #
65
+ def run
66
+ start
67
+
68
+ yield
69
+ rescue => e
70
+ @logger.fatal("FATAL ERROR: #{e.message} #{e.backtrace.join("\n")}")
71
+ raise
72
+ ensure
73
+ stop
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pact/ffi/verifier'
4
+ require 'pact/native/logger'
5
+
6
+ module Pact
7
+ module Provider
8
+ class HttpVerifier < BaseVerifier
9
+ PROVIDER_TRANSPORT_TYPE = 'http'
10
+
11
+ def initialize(pact_config, mixed_config = nil)
12
+ super
13
+
14
+ unless pact_config.is_a?(::Pact::Provider::PactConfig::Http)
15
+ raise ArgumentError,
16
+ 'pact_config must be an instance of Pact::Provider::PactConfig::Http'
17
+ end
18
+
19
+ @http_server = HttpServer.new(host: '127.0.0.1', port: @pact_config.http_port, app: @pact_config.app,
20
+ logger: @pact_config.logger)
21
+ end
22
+
23
+ private
24
+
25
+ def set_provider_info(pact_handle)
26
+ PactFfi::Verifier.set_provider_info(pact_handle, @pact_config.provider_name, '', '', @pact_config.http_port, '')
27
+ end
28
+
29
+ def add_provider_transport(pact_handle)
30
+ # The http transport is already added when the `set_provider_info` method is called,
31
+ # so we don't need to explicitly add the transport here
32
+ end
33
+
34
+ def start_servers!
35
+ super
36
+ @http_server.start
37
+ end
38
+
39
+ def stop_servers
40
+ super
41
+ @http_server.stop
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webrick"
4
+
5
+ module Pact
6
+ module Provider
7
+ class MessageProviderServlet < WEBrick::HTTPServlet::ProcHandler
8
+ attr_reader :logger
9
+
10
+ CONTENT_TYPE_JSON = "application/json"
11
+ CONTENT_TYPE_PROTO = "application/protobuf"
12
+ METADATA_HEADER = "pact-message-metadata"
13
+
14
+ def initialize(logger: nil)
15
+ super(build_proc)
16
+
17
+ @message_handlers = {}
18
+
19
+ @logger = logger || Logger.new($stdout)
20
+ end
21
+
22
+ def add_message_handler(name, &block)
23
+ raise "message handler for #{name} already configured" if @message_handlers[name].present?
24
+
25
+ @message_handlers[name] = {proc: block}
26
+ end
27
+
28
+ private
29
+
30
+ def build_proc
31
+ proc do |request, response|
32
+ # {"description":"message: ","providerStates":[{"name":"pet exists","params":{"pet_id":1}}]}
33
+ data = JSON.parse(request.body)
34
+
35
+ description = data["description"]
36
+ provider_states = data["providerStates"]
37
+
38
+ body, metadata = handle(description, provider_states)
39
+
40
+ response.status = 200
41
+ if body.is_a?(String)
42
+ # protobuf-serialized body
43
+ response.body = body
44
+ response.content_type = metadata[:content_type] || CONTENT_TYPE_PROTO
45
+ else
46
+ response.body = body.to_json
47
+ response.content_type = CONTENT_TYPE_JSON
48
+ end
49
+ response[METADATA_HEADER] = Base64.urlsafe_encode64(metadata.to_json)
50
+ rescue JSON::ParserError => ex
51
+ logger.error("cannot parse request: #{ex.message}")
52
+ response.status = 500
53
+ rescue => ex
54
+ logger.error("cannot handle message request: #{ex.message}")
55
+ response.status = 500
56
+ end
57
+ end
58
+
59
+ def handle(description, provider_states)
60
+ handler = find_handler_for(description)
61
+ return {}, {} unless handler
62
+
63
+ body, metadata = handler[:proc].call(provider_states&.first || {})
64
+ unless metadata[:content_type]
65
+ # try to find content-type in provider states
66
+ content_type = provider_states&.filter_map { |state| state.dig("params", "contentType") }&.first
67
+ metadata[:content_type] = content_type if content_type
68
+ end
69
+ [body, metadata]
70
+ end
71
+
72
+ def find_handler_for(description)
73
+ @message_handlers[description]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ # # frozen_string_literal: true
2
+ module Pact
3
+ module Provider
4
+ # MixedVerifier coordinates verification for all present configs (async, grpc, http)
5
+ class MixedVerifier
6
+ attr_reader :mixed_config, :verifiers
7
+
8
+ def initialize(mixed_config)
9
+ unless mixed_config.is_a?(::Pact::Provider::PactConfig::Mixed)
10
+ raise ArgumentError, 'mixed_config must be a PactConfig::Mixed'
11
+ end
12
+
13
+ @mixed_config = mixed_config
14
+ @verifiers = []
15
+ @verifiers << AsyncMessageVerifier.new(mixed_config.async_config) if mixed_config.async_config
16
+ @verifiers << GrpcVerifier.new(mixed_config.grpc_config) if mixed_config.grpc_config
17
+ @verifiers << HttpVerifier.new(mixed_config.http_config) if mixed_config.http_config
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack-proxy'
4
+
5
+ module Pact
6
+ module Provider
7
+ class PactBrokerProxy < Rack::Proxy
8
+ attr_reader :backend_uri, :path, :logger
9
+
10
+ # e.g. /pacts/provider/paas-stand-seeker/consumer/paas-stand-placer/pact-version/2967a9343bd8fdd28a286c4b8322380020618892/metadata/c1tdW2VdPXByb2R1Y3Rpb24mc1tdW2N2XT03MzIy # rubocop:disable Layout/LineLength
11
+ PACT_FILE_REQUEST_PATH_REGEX = %r{/pacts/provider/.+?/consumer/.+?/pact-version/.+}.freeze
12
+
13
+ def initialize(app = nil, opts = {})
14
+ super
15
+ @backend_uri = URI(opts[:backend])
16
+ @path = nil
17
+ @logger = opts[:logger] || Logger.new($stdout)
18
+ end
19
+
20
+ def perform_request(env)
21
+ request = Rack::Request.new(env)
22
+ env['rack.timeout'] ||= ENV.fetch('PACT_BROKER_REQUEST_TIMEOUT', 5).to_i
23
+ @path = request.path
24
+
25
+ super
26
+ end
27
+
28
+ def rewrite_env(env)
29
+ env['HTTP_HOST'] = backend_uri.host
30
+ env
31
+ end
32
+
33
+ def rewrite_response(triplet)
34
+ status, headers, body = triplet
35
+
36
+ if status == '200' && PACT_FILE_REQUEST_PATH_REGEX.match?(path)
37
+ patched_body = patch_response(body.first)
38
+
39
+ # we need to recalculate content length
40
+ headers[Rack::CONTENT_LENGTH] = patched_body.bytesize.to_s
41
+
42
+ return [status, headers, [patched_body]]
43
+ end
44
+
45
+ triplet
46
+ end
47
+
48
+ private
49
+
50
+ def patch_response(raw_body)
51
+ parsed_body = JSON.parse(raw_body)
52
+
53
+ return body if parsed_body['consumer'].blank? || parsed_body['provider'].blank?
54
+ return body if parsed_body['interactions'].blank?
55
+
56
+ JSON.generate(parsed_body)
57
+ rescue JSON::ParserError => e
58
+ logger.error("cannot parse broker response: #{e.message}")
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webrick"
4
+
5
+ module Pact
6
+ module Provider
7
+ class PactBrokerProxyRunner
8
+ attr_reader :logger
9
+
10
+
11
+ def initialize(pact_broker_host:, port: 9002, host: "127.0.0.1", pact_broker_user: nil, pact_broker_password: nil, pact_broker_token: nil, logger: nil)
12
+ @host = host
13
+ @port = port
14
+ @pact_broker_host = pact_broker_host
15
+ @pact_broker_user = pact_broker_user
16
+ @pact_broker_password = pact_broker_password
17
+ @pact_broker_token = pact_broker_token
18
+ @logger = logger || Logger.new($stdout)
19
+
20
+ @thread = nil
21
+ end
22
+
23
+ def proxy_url
24
+ "http://#{@host}:#{@port}"
25
+ end
26
+
27
+ def start
28
+ raise "server already running, stop server before starting new one" if @thread
29
+ # Rack 2/3 compatibility
30
+ begin
31
+ require 'rack/handler/webrick'
32
+ handler = ::Rack::Handler::WEBrick
33
+ rescue LoadError
34
+ require 'rackup/handler/webrick'
35
+ handler = Class.new(Rackup::Handler::WEBrick)
36
+ end
37
+ @server = WEBrick::HTTPServer.new(
38
+ { BindAddress: @host, Port: @port, Logger: @logger, AccessLog: [] },
39
+ WEBrick::Config::HTTP
40
+ )
41
+ @server.mount("/", handler, PactBrokerProxy.new(
42
+ nil,
43
+ backend: @pact_broker_host,
44
+ streaming: false,
45
+ username: @pact_broker_user || nil,
46
+ password: @pact_broker_password || nil,
47
+ token: @pact_broker_token || nil,
48
+ logger: @logger
49
+ ))
50
+
51
+ @thread = Thread.new do
52
+ @logger.debug "starting pact broker proxy server"
53
+ @server.start
54
+ end
55
+ end
56
+
57
+ def stop
58
+ @logger.info("stopping pact broker proxy server")
59
+
60
+ @server&.shutdown
61
+ @thread&.join
62
+
63
+ @logger.info("pact broker proxy server stopped")
64
+ end
65
+
66
+ def run
67
+ start
68
+
69
+ yield
70
+ rescue => e
71
+ logger.fatal("FATAL ERROR: #{e.message} #{e.backtrace.join("\n")}")
72
+ raise
73
+ ensure
74
+ stop
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Pact
6
+ module Provider
7
+ module PactConfig
8
+ class Async < Base
9
+ def initialize(provider_name:, opts: {})
10
+ super
11
+ handlers = opts[:message_handlers] || {}
12
+ handlers.each do |name, block|
13
+ new_message_handler(name, &block)
14
+ end
15
+ end
16
+
17
+ def new_message_handler(name, opts: {}, &block)
18
+ provider_setup_server.add_message_handler(name, &block)
19
+ end
20
+
21
+ def new_verifier(config = nil)
22
+ AsyncMessageVerifier.new(self, config)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pact
4
+ module Provider
5
+ module PactConfig
6
+ class Base
7
+ attr_reader :provider_name, :provider_version, :log_level, :provider_setup_server, :provider_setup_port, :pact_proxy_port,
8
+ :consumer_branch, :consumer_version, :consumer_name, :broker_url, :broker_username, :broker_password, :verify_only, :pact_dir,
9
+ :pact_uri, :provider_version_branch, :provider_version_tags, :consumer_version_selectors, :enable_pending, :include_wip_pacts_since,
10
+ :fail_if_no_pacts_found, :provider_build_uri, :broker_token, :consumer_version_tags, :publish_verification_results, :logger
11
+
12
+
13
+ def initialize(provider_name:, opts: {})
14
+ @provider_name = provider_name
15
+ @log_level = opts[:log_level] || :info
16
+ @pact_dir = opts[:pact_dir] || nil
17
+ @logger = opts[:logger] || nil
18
+ @provider_setup_port = opts[:provider_setup_port] || 9001
19
+ @pact_proxy_port = opts[:pact_proxy_port] || 9002
20
+ @pact_uri = ENV.fetch("PACT_URL", nil) || opts.fetch(:pact_uri, nil)
21
+ @publish_verification_results = ENV.fetch("PACT_PUBLISH_VERIFICATION_RESULTS", nil) == "true" || opts.fetch(:publish_verification_results, false)
22
+ @provider_version = ENV.fetch("PACT_PROVIDER_VERSION", nil) || opts.fetch(:provider_version, nil)
23
+ @provider_build_uri = ENV.fetch("PACT_PROVIDER_BUILD_URL", nil) || opts.fetch(:provider_build_uri, nil)
24
+ @provider_version_branch = ENV.fetch("PACT_PROVIDER_BRANCH", nil) || opts.fetch(:provider_version_branch, nil)
25
+ @provider_version_tags = ENV.fetch("PACT_PROVIDER_VERSION_TAGS", nil) || opts.fetch(:provider_version_tags, [])
26
+ @consumer_version_tags = ENV.fetch("PACT_CONSUMER_VERSION_TAGS", nil) || opts.fetch(:consumer_version_tags, [])
27
+ @consumer_version_selectors = ENV.fetch("PACT_CONSUMER_VERSION_SELECTORS", nil) || opts.fetch(:consumer_version_selectors, nil)
28
+ @enable_pending = ENV.fetch("PACT_VERIFIER_ENABLE_PENDING", nil) == "true" || opts.fetch(:enable_pending, false)
29
+ @include_wip_pacts_since = ENV.fetch("PACT_INCLUDE_WIP_PACTS_SINCE", nil) || opts.fetch(:include_wip_pacts_since, nil)
30
+ @fail_if_no_pacts_found = ENV.fetch("PACT_FAIL_IF_NO_PACTS_FOUND", nil) == "true" || opts.fetch(:fail_if_no_pacts_found, true)
31
+ @consumer_branch = ENV.fetch("PACT_CONSUMER_BRANCH", nil) || opts.fetch(:consumer_branch, nil)
32
+ @consumer_version = ENV.fetch("PACT_CONSUMER_VERSION", nil) || opts.fetch(:consumer_version, nil)
33
+ @consumer_name = opts[:consumer_name]
34
+ @broker_url = ENV.fetch("PACT_BROKER_BASE_URL", nil) || opts.fetch(:broker_url, nil)
35
+ @broker_username = ENV.fetch("PACT_BROKER_USERNAME", nil) || opts.fetch(:broker_username, nil)
36
+ @broker_password = ENV.fetch("PACT_BROKER_PASSWORD", nil) || opts.fetch(:broker_password, nil)
37
+ @broker_token = ENV.fetch("PACT_BROKER_TOKEN", nil) || opts.fetch(:broker_token, nil)
38
+ @verify_only = [ENV.fetch("PACT_CONSUMER_FULL_NAME", nil)].compact || opts.fetch(:verify_only, [])
39
+
40
+ @provider_setup_server = opts[:provider_setup_server] || ProviderServerRunner.new(port: @provider_setup_port, logger: @logger)
41
+ if @broker_url.present?
42
+ @pact_proxy_server = PactBrokerProxyRunner.new(
43
+ port: @pact_proxy_port,
44
+ pact_broker_host: @broker_url,
45
+ pact_broker_user: @broker_username,
46
+ pact_broker_password: @broker_password,
47
+ pact_broker_token: @broker_token,
48
+ logger: @logger
49
+ )
50
+ end
51
+ end
52
+
53
+
54
+ def start_servers
55
+ @provider_setup_server.start
56
+ @pact_proxy_server&.start
57
+ end
58
+
59
+ def stop_servers
60
+ @provider_setup_server.stop
61
+ @pact_proxy_server&.stop
62
+ end
63
+
64
+ def provider_setup_url
65
+ @provider_setup_server.state_setup_url
66
+ end
67
+
68
+ def message_setup_url # rubocop:disable Rails/Delegate
69
+ @provider_setup_server.message_setup_url
70
+ end
71
+
72
+ def pact_broker_proxy_url
73
+ @pact_proxy_server&.proxy_url
74
+ end
75
+
76
+ def new_provider_state(name, opts: {}, &block)
77
+ config = ProviderStateConfiguration.new(name, opts: opts)
78
+ config.instance_eval(&block)
79
+ config.validate!
80
+
81
+ use_hooks = !opts[:skip_hooks]
82
+
83
+ @provider_setup_server.add_setup_state(name, use_hooks, &config.setup_proc) if config.setup_proc
84
+ @provider_setup_server.add_teardown_state(name, use_hooks, &config.teardown_proc) if config.teardown_proc
85
+ end
86
+
87
+ def before_setup(&block)
88
+ @provider_setup_server.set_before_setup_hook(&block)
89
+ end
90
+
91
+ def after_teardown(&block)
92
+ @provider_setup_server.set_after_teardown_hook(&block)
93
+ end
94
+
95
+ def new_verifier
96
+ raise PactImplementationRequired, "#new_verifier should be implemented"
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Pact
6
+ module Provider
7
+ module PactConfig
8
+ class Grpc < Base
9
+ attr_reader :grpc_port, :grpc_services, :grpc_server
10
+
11
+ def initialize(provider_name:, opts: {})
12
+ super
13
+
14
+ @grpc_port = opts[:grpc_port] || 0
15
+ @grpc_services = opts[:grpc_services] || []
16
+ end
17
+
18
+ def new_verifier(config = nil)
19
+ GrpcVerifier.new(self, config)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Pact
6
+ module Provider
7
+ module PactConfig
8
+ class Http < Base
9
+ attr_reader :http_port
10
+ attr_reader :app
11
+
12
+ def initialize(provider_name:, opts: {})
13
+ super
14
+
15
+ @http_port = opts[:http_port] || 0
16
+ @app = opts[:app] || nil
17
+ end
18
+
19
+ def new_verifier(config = nil)
20
+ HttpVerifier.new(self, config)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end