pact_broker 2.59.1 → 2.62.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +53 -0
  3. data/README.md +1 -1
  4. data/db/migrations/20200922_add_event_to_triggered_webhook.rb +5 -0
  5. data/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema.rb +10 -3
  6. data/lib/pact_broker/api/contracts/verifiable_pacts_query_schema.rb +1 -0
  7. data/lib/pact_broker/api/decorators/triggered_webhook_decorator.rb +1 -0
  8. data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +5 -5
  9. data/lib/pact_broker/api/pact_broker_urls.rb +9 -2
  10. data/lib/pact_broker/api/resources/all_webhooks.rb +1 -6
  11. data/lib/pact_broker/api/resources/can_i_deploy.rb +0 -4
  12. data/lib/pact_broker/api/resources/default_base_resource.rb +9 -0
  13. data/lib/pact_broker/api/resources/error_handler.rb +2 -4
  14. data/lib/pact_broker/api/resources/error_test.rb +4 -2
  15. data/lib/pact_broker/api/resources/group.rb +0 -4
  16. data/lib/pact_broker/api/resources/index.rb +11 -8
  17. data/lib/pact_broker/api/resources/integration.rb +0 -4
  18. data/lib/pact_broker/api/resources/integrations.rb +0 -4
  19. data/lib/pact_broker/api/resources/label.rb +0 -8
  20. data/lib/pact_broker/api/resources/latest_pact.rb +1 -5
  21. data/lib/pact_broker/api/resources/latest_pacts.rb +1 -5
  22. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +0 -4
  23. data/lib/pact_broker/api/resources/matrix.rb +4 -0
  24. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +4 -0
  25. data/lib/pact_broker/api/resources/metrics.rb +4 -0
  26. data/lib/pact_broker/api/resources/pact.rb +4 -8
  27. data/lib/pact_broker/api/resources/pact_content_diff.rb +4 -0
  28. data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +5 -1
  29. data/lib/pact_broker/api/resources/pact_versions.rb +5 -1
  30. data/lib/pact_broker/api/resources/pact_webhooks.rb +5 -1
  31. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +5 -1
  32. data/lib/pact_broker/api/resources/pacticipant.rb +5 -9
  33. data/lib/pact_broker/api/resources/pacticipants.rb +0 -4
  34. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +4 -0
  35. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +4 -9
  36. data/lib/pact_broker/api/resources/provider_pacts.rb +1 -5
  37. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +8 -6
  38. data/lib/pact_broker/api/resources/relationships.rb +4 -2
  39. data/lib/pact_broker/api/resources/tag.rb +5 -5
  40. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +5 -2
  41. data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +7 -5
  42. data/lib/pact_broker/api/resources/verification.rb +5 -5
  43. data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +6 -12
  44. data/lib/pact_broker/api/resources/verifications.rb +5 -5
  45. data/lib/pact_broker/api/resources/version.rb +5 -5
  46. data/lib/pact_broker/api/resources/versions.rb +5 -2
  47. data/lib/pact_broker/api/resources/webhook.rb +4 -0
  48. data/lib/pact_broker/api/resources/webhook_execution.rb +4 -0
  49. data/lib/pact_broker/api/resources/webhooks.rb +5 -1
  50. data/lib/pact_broker/app.rb +1 -1
  51. data/lib/pact_broker/badges/service.rb +1 -1
  52. data/lib/pact_broker/certificates/service.rb +2 -2
  53. data/lib/pact_broker/configuration.rb +21 -1
  54. data/lib/pact_broker/db.rb +14 -0
  55. data/lib/pact_broker/db/table_dependency_calculator.rb +45 -0
  56. data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +78 -0
  57. data/lib/pact_broker/domain/pact.rb +9 -0
  58. data/lib/pact_broker/domain/webhook_request.rb +3 -1
  59. data/lib/pact_broker/matrix/deployment_status_summary.rb +1 -1
  60. data/lib/pact_broker/pacts/build_verifiable_pact_notices.rb +7 -4
  61. data/lib/pact_broker/pacts/content.rb +30 -5
  62. data/lib/pact_broker/pacts/repository.rb +25 -31
  63. data/lib/pact_broker/pacts/selector.rb +8 -0
  64. data/lib/pact_broker/test/test_data_builder.rb +1 -1
  65. data/lib/pact_broker/version.rb +1 -1
  66. data/lib/pact_broker/webhooks/job.rb +8 -2
  67. data/lib/pact_broker/webhooks/repository.rb +3 -2
  68. data/lib/pact_broker/webhooks/service.rb +2 -2
  69. data/lib/pact_broker/webhooks/triggered_webhook.rb +1 -3
  70. data/lib/pact_broker/webhooks/webhook_event.rb +1 -2
  71. data/script/seed.rb +39 -17
  72. data/spec/features/get_provider_pacts_for_verification_spec.rb +8 -0
  73. data/spec/lib/pact_broker/api/contracts/verifiable_pacts_json_query_schema_spec.rb +23 -4
  74. data/spec/lib/pact_broker/api/decorators/pact_webhooks_status_decorator_spec.rb +2 -1
  75. data/spec/lib/pact_broker/api/decorators/triggered_webhook_decorator_spec.rb +3 -1
  76. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +15 -0
  77. data/spec/lib/pact_broker/api/resources/default_base_resource_spec.rb +10 -5
  78. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +18 -1
  79. data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +1 -1
  80. data/spec/lib/pact_broker/api/resources/verification_triggered_webhooks_spec.rb +0 -1
  81. data/spec/lib/pact_broker/badges/service_spec.rb +1 -1
  82. data/spec/lib/pact_broker/certificates/service_spec.rb +3 -3
  83. data/spec/lib/pact_broker/pacts/content_spec.rb +90 -0
  84. data/spec/lib/pact_broker/pacts/repository_find_for_verification_fallback_spec.rb +14 -0
  85. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +62 -0
  86. data/spec/lib/pact_broker/webhooks/job_spec.rb +19 -1
  87. data/spec/lib/pact_broker/webhooks/repository_spec.rb +3 -2
  88. data/spec/lib/pact_broker/webhooks/service_spec.rb +2 -2
  89. data/spec/support/database_cleaner.rb +1 -5
  90. data/tasks/database.rb +1 -0
  91. data/tasks/database/table_dependency_calculator.rb +4 -41
  92. metadata +5 -2
@@ -6,7 +6,6 @@ module PactBroker
6
6
  module Api
7
7
  module Resources
8
8
  class Versions < BaseResource
9
-
10
9
  def content_types_provided
11
10
  [["application/hal+json", :to_json]]
12
11
  end
@@ -24,7 +23,11 @@ module PactBroker
24
23
  end
25
24
 
26
25
  def versions
27
- pacticipant_service.find_all_pacticipant_versions_in_reverse_order pacticipant_name
26
+ @versions ||= pacticipant_service.find_all_pacticipant_versions_in_reverse_order pacticipant_name
27
+ end
28
+
29
+ def policy_name
30
+ :'versions::versions'
28
31
  end
29
32
  end
30
33
  end
@@ -57,6 +57,10 @@ module PactBroker
57
57
  true
58
58
  end
59
59
 
60
+ def policy_name
61
+ :'webhooks::webhook'
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def webhook
@@ -48,6 +48,10 @@ module PactBroker
48
48
  end
49
49
  end
50
50
 
51
+ def policy_name
52
+ :'webhooks::webhook'
53
+ end
54
+
51
55
  private
52
56
 
53
57
  def post_response_body webhook_execution_result
@@ -50,10 +50,14 @@ module PactBroker
50
50
  Decorators::WebhooksDecorator.new(webhooks).to_json(decorator_options(resource_title: 'Pact webhooks'))
51
51
  end
52
52
 
53
+ def policy_name
54
+ :'webhooks::webhooks'
55
+ end
56
+
53
57
  private
54
58
 
55
59
  def webhooks
56
- webhook_service.find_by_consumer_and_provider consumer, provider
60
+ webhook_service.find_by_consumer_and_provider(consumer, provider)
57
61
  end
58
62
 
59
63
  def webhook
@@ -227,7 +227,7 @@ module PactBroker
227
227
 
228
228
  def configure_sucker_punch
229
229
  SuckerPunch.exception_handler = -> (ex, klass, args) do
230
- PactBroker.log_error(ex, "Unhandled Suckerpunch error for #{klass}.perform(#{args.inspect})")
230
+ PactBroker.logger.warn("Unhandled Suckerpunch error for #{klass}.perform(#{args.inspect})", ex)
231
231
  end
232
232
  end
233
233
 
@@ -100,7 +100,7 @@ module PactBroker
100
100
  logger.warn "Timeout retrieving badge from #{uri} #{e.class} - #{e.message}"
101
101
  nil
102
102
  rescue StandardError => e
103
- log_error e, "Error retrieving badge from #{uri}"
103
+ logger.warn("Error retrieving badge from #{uri}", e)
104
104
  nil
105
105
  end
106
106
  end
@@ -18,7 +18,7 @@ module PactBroker
18
18
  logger.debug("Loading certificate #{certificate.subject} in to cert store")
19
19
  cert_store.add_cert(certificate)
20
20
  rescue StandardError => e
21
- log_error e, "Error adding certificate object #{certificate.to_s} to store"
21
+ logger.warn("Error adding certificate object #{certificate.to_s} to store", e)
22
22
  end
23
23
  end
24
24
  cert_store
@@ -31,7 +31,7 @@ module PactBroker
31
31
  begin
32
32
  OpenSSL::X509::Certificate.new(c)
33
33
  rescue StandardError => e
34
- log_error e, "Error creating certificate object from certificate #{certificate.uuid} '#{certificate.description}'"
34
+ logger.warn("Error creating certificate object from certificate #{certificate.uuid} '#{certificate.description}'", e)
35
35
  nil
36
36
  end
37
37
  end
@@ -31,12 +31,19 @@ module PactBroker
31
31
  :webhook_host_whitelist,
32
32
  :base_equality_only_on_content_that_affects_verification_results,
33
33
  :seed_example_data,
34
- :badge_provider_mode
34
+ :badge_provider_mode,
35
+ :warning_error_class_names,
36
+ :base_url,
37
+ :log_dir,
38
+ :allow_missing_migration_files,
39
+ :auto_migrate_db_data,
40
+ :use_rack_protection
35
41
  ]
36
42
 
37
43
  attr_accessor :base_url, :log_dir, :database_connection, :auto_migrate_db, :auto_migrate_db_data, :allow_missing_migration_files, :example_data_seeder, :seed_example_data, :use_hal_browser, :html_pact_renderer, :use_rack_protection
38
44
  attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser, :sha_generator
39
45
  attr_accessor :use_case_sensitive_resource_names, :order_versions_by_date
46
+ attr_accessor :warning_error_class_names
40
47
  attr_accessor :check_for_potential_duplicate_pacticipant_names
41
48
  attr_accessor :webhook_retry_schedule
42
49
  attr_reader :webhook_http_method_whitelist, :webhook_scheme_whitelist, :webhook_host_whitelist
@@ -115,6 +122,7 @@ module PactBroker
115
122
  require 'pact_broker/api/resources/default_base_resource'
116
123
  PactBroker::Api::Resources::DefaultBaseResource
117
124
  }
125
+ config.warning_error_class_names = ['Sequel::ForeignKeyConstraintViolation']
118
126
  config
119
127
  end
120
128
 
@@ -126,6 +134,12 @@ module PactBroker
126
134
  @custom_logger = logger
127
135
  end
128
136
 
137
+ def log_configuration
138
+ SAVABLE_SETTING_NAMES.sort.each do | setting |
139
+ logger.info "PactBroker.configuration.#{setting}=#{PactBroker.configuration.send(setting).inspect}"
140
+ end
141
+ end
142
+
129
143
  def self.default_html_pact_render
130
144
  lambda { |pact, options|
131
145
  require 'pact_broker/api/renderers/html_pact_renderer'
@@ -228,6 +242,12 @@ module PactBroker
228
242
  @webhook_host_whitelist = parse_space_delimited_string_list_property('webhook_host_whitelist', webhook_host_whitelist)
229
243
  end
230
244
 
245
+ def warning_error_classes
246
+ warning_error_class_names.collect do | class_name |
247
+ Object.const_get(class_name)
248
+ end
249
+ end
250
+
231
251
  private
232
252
 
233
253
  def parse_space_delimited_string_list_property property_name, property_value
@@ -3,6 +3,7 @@ require 'pact_broker/db/validate_encoding'
3
3
  require 'pact_broker/db/migrate'
4
4
  require 'pact_broker/db/migrate_data'
5
5
  require 'pact_broker/db/version'
6
+ require 'pact_broker/db/table_dependency_calculator'
6
7
 
7
8
  Sequel.datetime_class = DateTime
8
9
 
@@ -31,6 +32,19 @@ module PactBroker
31
32
  Sequel::TimestampMigrator.is_current?(database_connection, PactBroker::DB::MIGRATIONS_DIR, options)
32
33
  end
33
34
 
35
+ def self.truncate database_connection, options = {}
36
+ exceptions = options[:except] || []
37
+ TableDependencyCalculator.call(database_connection).each do | table_name |
38
+ if !exceptions.include?(table_name)
39
+ begin
40
+ database_connection[table_name].truncate
41
+ rescue StandardError => e
42
+ puts "Could not truncate table #{table_name}"
43
+ end
44
+ end
45
+ end
46
+ end
47
+
34
48
  def self.version database_connection
35
49
  PactBroker::DB::Version.call(database_connection)
36
50
  end
@@ -0,0 +1,45 @@
1
+ module PactBroker
2
+ module DB
3
+
4
+ # Returns a list of the tables in the database in the order in which
5
+ # they can be truncated or dropped
6
+ class TableDependencyCalculator
7
+
8
+ def self.call connection
9
+ new(connection).call
10
+ end
11
+
12
+ def initialize connection
13
+ @connection = connection
14
+ end
15
+
16
+ def call
17
+ ordered_table_names = []
18
+ dependencies = @connection
19
+ .tables
20
+ .collect{|it| @connection.foreign_key_list(it)
21
+ .collect{|fk| {from: it, to: fk[:table]} } }
22
+ .flatten
23
+ .uniq
24
+ table_names = @connection.tables - [:schema_migrations, :schema_info]
25
+ check(table_names, dependencies, ordered_table_names)
26
+ ordered_table_names
27
+ end
28
+
29
+ def deps_on table_name, deps
30
+ deps.select{ | d| d[:to] == table_name }.collect{ |d| d[:from] }
31
+ end
32
+
33
+ def check table_names, deps, ordered_table_names
34
+ return if table_names.size == 0
35
+ remaining_dependencies = deps_on(table_names.first, deps) - ordered_table_names
36
+ if remaining_dependencies.size == 0
37
+ ordered_table_names << table_names.first
38
+ check(table_names[1..-1], deps, ordered_table_names)
39
+ else
40
+ check((remaining_dependencies + table_names).uniq, deps, ordered_table_names)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,78 @@
1
+ # Provider pacts for verification
2
+
3
+ Path: `/pacts/provider/{provider}/for-verification`
4
+
5
+ Allowed methods: `POST`
6
+
7
+ Content type: `application/hal+json`
8
+
9
+ Returns a deduplicated list of pacts to be verified by the specified provider.
10
+
11
+ ### Body
12
+
13
+ Example: This data structure represents the way a user might specify "I want to verify the latest 'master' pact, all 'prod' pacts, and when I publish the verification results, I'm going to tag the provider version with 'master'"
14
+
15
+ {
16
+ "consumerVersionSelectors": [
17
+ {
18
+ "tag": "master",
19
+ "latest": true
20
+ },{
21
+ "tag": "prod"
22
+ }
23
+ ],
24
+ "providerVersionTags": ["master"],
25
+ "includePendingStatus": true,
26
+ "includeWipPactsSince": "2020-01-01"
27
+ }
28
+
29
+
30
+ `consumerVersionSelectors.tag`: the tag name(s) of the consumer versions to get the pacts for.
31
+
32
+ `consumerVersionSelectors.fallbackTag`: the name of the tag to fallback to if the specified `tag` does not exist. This is useful when the consumer and provider use matching branch names to coordinate the development of new features.
33
+
34
+ `consumerVersionSelectors.latest`: true. If the latest flag is omitted, *all* the pacts with the specified tag will be returned. (This might seem a bit weird, but it's done this way to match the syntax used for the matrix query params. See https://docs.pact.io/selectors)
35
+
36
+ `consumerVersionSelectors.consumer`: allows a selector to only be applied to a certain consumer. This is used when there is an API that has multiple consumers, one of which is a deployed service, and one of which is a mobile consumer. The deployed service only needs the latest production pact verified, where as the mobile consumer may want all the production pacts verified.
37
+
38
+ `providerVersionTags`: the tag name(s) for the provider application version that will be published with the verification results. This is used by the Broker to determine whether or not a particular pact is in pending state or not. This parameter can be specified multiple times.
39
+
40
+ `includePendingStatus`: true|false (default false). When true, a pending boolean will be added to the verificationProperties in the response, and an extra message will appear in the notices array to indicate why this pact is/is not in pending state. This will allow your code to handle the response based on only what is present in the response, and not have to do ifs based on the user's options together with the response. As requested in the "pacts for verification" issue, please print out these messages in the tests if possible. If not possible, perhaps create a separate task which will list the pact URLs and messages for debugging purposes.
41
+
42
+ `includeWipPactsSince`: Date string. The date from which to include the "work in progress" pacts. See https://docs.pact.io/wip for more information on work in progress pacts.
43
+
44
+ ### Response body
45
+
46
+ `pending` flag and the "pending reason" notice will only be included if `includePendingStatus` is set to true.
47
+
48
+
49
+ {
50
+ "_embedded": {
51
+ "pacts": [
52
+ {
53
+ "verificationProperties": {
54
+ "notices": [
55
+ {
56
+ "text": "This pact is being verified because it is the pact for the latest version of Foo tagged with 'dev'",
57
+ "when": "before_verification"
58
+ }
59
+ ],
60
+ "pending": false
61
+ },
62
+ "_links": {
63
+ "self": {
64
+ "href": "http://localhost:9292/pacts/provider/Bar/consumer/Foo/pact-version/0e3369199f4008231946e0245474537443ccda2a",
65
+ "name": "Pact between Foo (v1.0.0) and Bar"
66
+ }
67
+ }
68
+ }
69
+ ]
70
+ },
71
+ "_links": {
72
+ "self": {
73
+ "href": "http://localhost:9292/pacts/provider/Bar/for-verification",
74
+ "title": "Pacts to be verified"
75
+ }
76
+ }
77
+ }
78
+
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/db'
2
2
  require 'pact_broker/json'
3
+ require 'pact_broker/pacts/content'
3
4
 
4
5
  =begin
5
6
  This class most accurately represents a PactPublication
@@ -76,6 +77,14 @@ module PactBroker
76
77
  JSON.parse(json_content, PACT_PARSING_OPTIONS)
77
78
  end
78
79
 
80
+ def content_object
81
+ @content_object ||= begin
82
+ PactBroker::Pacts::Content.from_json(json_content)
83
+ rescue
84
+ PactBroker::Pacts::Content.new
85
+ end
86
+ end
87
+
79
88
  def pact_publication_id
80
89
  id
81
90
  end
@@ -59,7 +59,9 @@ module PactBroker
59
59
 
60
60
  def http_request
61
61
  @http_request ||= begin
62
- req = Net::HTTP.const_get(method.capitalize).new(url)
62
+ req = Net::HTTP.const_get(method.capitalize).new(uri.request_uri)
63
+ req.delete("accept-encoding")
64
+ req.delete("user-agent")
63
65
  headers.each_pair { | name, value | req[name] = value }
64
66
  req.basic_auth(username, password) if username && username.size > 0
65
67
  req.body = body unless body.nil?
@@ -195,7 +195,7 @@ module PactBroker
195
195
  InteractionsMissingVerifications.new(selector_for(row.consumer_name), selector_for(row.provider_name), row.verification.interactions_missing_test_results)
196
196
  end
197
197
  rescue StandardError => e
198
- log_error(e, "Error determining if there were missing interaction verifications")
198
+ logger.warn("Error determining if there were missing interaction verifications", e)
199
199
  nil
200
200
  end
201
201
  end.compact.tap { |it| report_missing_interaction_verifications(it) if it.any? }
@@ -7,10 +7,13 @@ module PactBroker
7
7
  def self.call(verifiable_pact, pact_url, options)
8
8
  messages = VerifiablePactMessages.new(verifiable_pact, pact_url)
9
9
 
10
- notices = [{
11
- when: 'before_verification',
12
- text: messages.inclusion_reason
13
- }]
10
+ notices = []
11
+
12
+ if options[:deprecated]
13
+ append_notice(notices, 'before_verification', 'WARNING - this version of the Pact library uses a beta version of the API which will be removed in the future. Please upgrade your Pact library. See https://docs.pact.io/pact_broker/advanced_topics/provider_verification_results/#pacts-for-verification for minimum required versions.')
14
+ end
15
+
16
+ append_notice(notices, 'before_verification', messages.inclusion_reason)
14
17
 
15
18
  if options[:include_pending_status]
16
19
  append_notice(notices, 'before_verification', messages.pending_reason)
@@ -32,15 +32,22 @@ module PactBroker
32
32
  end
33
33
 
34
34
  def interactions_missing_test_results
35
- interactions.reject do | interaction |
35
+ return [] unless messages_or_interactions
36
+ messages_or_interactions.reject do | interaction |
36
37
  interaction['tests']&.any?
37
38
  end
38
39
  end
39
40
 
40
41
  def with_test_results(test_results)
41
- tests = test_results && test_results['tests']
42
- if tests.nil? || !tests.is_a?(Array) || tests.empty?
43
- tests = []
42
+ # new format
43
+ if test_results.is_a?(Array)
44
+ tests = test_results
45
+ else
46
+ # old format
47
+ tests = test_results && test_results['tests']
48
+ if tests.nil? || !tests.is_a?(Array) || tests.empty?
49
+ tests = []
50
+ end
44
51
  end
45
52
 
46
53
  new_pact_hash = pact_hash.dup
@@ -66,6 +73,12 @@ module PactBroker
66
73
  Content.from_hash(new_pact_hash)
67
74
  end
68
75
 
76
+ def interaction_ids
77
+ messages_or_interaction_or_empty_array.collect do | interaction |
78
+ interaction['_id']
79
+ end.compact
80
+ end
81
+
69
82
  # Half thinking this belongs in GenerateSha
70
83
  def content_that_affects_verification_results
71
84
  if interactions || messages
@@ -91,6 +104,10 @@ module PactBroker
91
104
  messages || interactions
92
105
  end
93
106
 
107
+ def messages_or_interaction_or_empty_array
108
+ messages_or_interactions || []
109
+ end
110
+
94
111
  def pact_specification_version
95
112
  maybe_pact_specification_version_1 = pact_hash['metadata']['pactSpecification']['version'] rescue nil
96
113
  maybe_pact_specification_version_2 = pact_hash['metadata']['pact-specification']['version'] rescue nil
@@ -129,7 +146,15 @@ module PactBroker
129
146
  end
130
147
 
131
148
  def test_is_for_interaction(interaction, test)
132
- test.is_a?(Hash) && interaction.is_a?(Hash) && test['interactionDescription'] == interaction['description'] && test['interactionProviderState'] == interaction['providerState']
149
+ test.is_a?(Hash) && interaction.is_a?(Hash) && ( interaction_ids_match(interaction, test) || description_and_state_match(interaction, test))
150
+ end
151
+
152
+ def interaction_ids_match(interaction, test)
153
+ interaction['_id'] && interaction['_id'] == test['interactionId']
154
+ end
155
+
156
+ def description_and_state_match(interaction, test)
157
+ test['interactionDescription'] && test['interactionDescription'] == interaction['description'] && test['interactionProviderState'] == interaction['providerState']
133
158
  end
134
159
  end
135
160
  end
@@ -405,42 +405,37 @@ module PactBroker
405
405
  SelectedPact.new(pact_publication.to_domain, Selectors.create_for_overall_latest)
406
406
  end
407
407
  else
408
- []
408
+ selectors_for_overall_latest = consumer_version_selectors.select(&:overall_latest?)
409
+ selectors_for_overall_latest.flat_map do | selector |
410
+ query = scope_for(LatestPactPublications).provider(provider_name)
411
+ query = query.consumer(selector.consumer) if selector.consumer
412
+ query.collect do | latest_pact_publication |
413
+ pact_publication = PactPublication.find(id: latest_pact_publication.id)
414
+ resolved_selector = selector.consumer ? Selector.latest_for_consumer(selector.consumer) : Selector.overall_latest
415
+ SelectedPact.new(pact_publication.to_domain, Selectors.new(resolved_selector))
416
+ end
417
+ end
409
418
  end
410
419
  end
411
420
 
412
421
  def find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors)
413
422
  # The tags for which only the latest version is specified
414
- tag_names = consumer_version_selectors.tag_names_of_selectors_for_latest_pacts
423
+ selectors = consumer_version_selectors.select(&:latest_for_tag?)
415
424
 
416
- # TODO make this an efficient query!
417
- # These are not yet de-duped. Should make the behaviour consistent between this and find_pacts_for_which_all_versions_for_the_tag_are_required ?
418
- if tag_names.any?
419
- scope_for(LatestTaggedPactPublications)
420
- .provider(provider_name)
421
- .where(tag_name: tag_names)
422
- .all
423
- .group_by(&:pact_version_id)
424
- .values
425
- .collect do | pact_publications |
426
- create_selected_pact(pact_publications)
427
- end
428
- else
429
- []
425
+ selectors.flat_map do | selector |
426
+ query = scope_for(LatestTaggedPactPublications).where(tag_name: selector.tag).provider(provider_name)
427
+ query = query.consumer(selector.consumer) if selector.consumer
428
+ query.all.collect do | latest_tagged_pact_publication |
429
+ pact_publication = PactPublication.find(id: latest_tagged_pact_publication.id)
430
+ resolved_pact = selector.consumer ? Selector.latest_for_tag_and_consumer(selector.tag, selector.consumer) : Selector.latest_for_tag(selector.tag)
431
+ SelectedPact.new(
432
+ pact_publication.to_domain,
433
+ Selectors.new(resolved_pact)
434
+ )
435
+ end
430
436
  end
431
437
  end
432
438
 
433
- def create_selected_pact(pact_publications)
434
- selector_tag_names = pact_publications.collect(&:tag_name)
435
- selectors = Selectors.create_for_latest_of_each_tag(selector_tag_names)
436
- last_pact_publication = pact_publications.sort_by(&:consumer_version_order).last
437
- pact_publication = scope_for(PactPublication).find(id: last_pact_publication.id)
438
- SelectedPact.new(
439
- pact_publication.to_domain,
440
- selectors
441
- )
442
- end
443
-
444
439
  def create_fallback_selected_pact(pact_publications, consumer_version_selectors)
445
440
  selector_tag_names = pact_publications.collect(&:tag_name)
446
441
  selectors = Selectors.create_for_latest_fallback_of_each_tag(selector_tag_names)
@@ -454,10 +449,9 @@ module PactBroker
454
449
 
455
450
  def find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(provider_name, selectors)
456
451
  selectors.collect do | selector |
457
- scope_for(LatestTaggedPactPublications)
458
- .provider(provider_name)
459
- .where(tag_name: selector.fallback_tag)
460
- .all
452
+ query = scope_for(LatestTaggedPactPublications).provider(provider_name).where(tag_name: selector.fallback_tag)
453
+ query = query.consumer(selector.consumer) if selector.consumer
454
+ query.all
461
455
  .collect do | latest_tagged_pact_publication |
462
456
  pact_publication = unscoped(PactPublication).find(id: latest_tagged_pact_publication.id)
463
457
  SelectedPact.new(