pact_broker 2.19.1 → 2.19.2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +0 -1
  4. data/db/migrations/000002_create_versions_table.rb +1 -1
  5. data/db/migrations/20180311_optimise_head_matrix.rb +1 -0
  6. data/db/migrations/20180523_create_latest_verifications_for_consumer_version_tags.rb +50 -0
  7. data/db/migrations/20180524_create_latest_verifications_for_consumer_and_provider.rb +46 -0
  8. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +7 -3
  9. data/lib/pact_broker/configuration.rb +6 -1
  10. data/lib/pact_broker/domain/webhook_request.rb +12 -11
  11. data/lib/pact_broker/index/service.rb +14 -84
  12. data/lib/pact_broker/matrix/aggregated_row.rb +71 -0
  13. data/lib/pact_broker/matrix/head_row.rb +23 -0
  14. data/lib/pact_broker/matrix/repository.rb +0 -1
  15. data/lib/pact_broker/matrix/row.rb +15 -20
  16. data/lib/pact_broker/pacts/content.rb +66 -0
  17. data/lib/pact_broker/pacts/create_formatted_diff.rb +0 -1
  18. data/lib/pact_broker/pacts/diff.rb +3 -2
  19. data/lib/pact_broker/pacts/generate_sha.rb +24 -0
  20. data/lib/pact_broker/pacts/parse.rb +11 -0
  21. data/lib/pact_broker/pacts/repository.rb +4 -4
  22. data/lib/pact_broker/pacts/service.rb +4 -2
  23. data/lib/pact_broker/pacts/sort_content.rb +54 -0
  24. data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +26 -0
  25. data/lib/pact_broker/verifications/latest_verification_for_consumer_version_tag.rb +23 -0
  26. data/lib/pact_broker/version.rb +1 -1
  27. data/lib/pact_broker/versions/repository.rb +12 -3
  28. data/script/run-with-ssl.rb +44 -0
  29. data/spec/features/base_equality_only_on_content_that_affects_verification_results_spec.rb +34 -0
  30. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +6 -2
  31. data/spec/lib/pact_broker/domain/{index_items_spec.rb → index_item_spec.rb} +0 -0
  32. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +16 -5
  33. data/spec/lib/pact_broker/matrix/aggregated_row_spec.rb +79 -0
  34. data/spec/lib/pact_broker/matrix/head_row_spec.rb +55 -0
  35. data/spec/lib/pact_broker/matrix/row_spec.rb +22 -2
  36. data/spec/lib/pact_broker/pacts/content_spec.rb +166 -0
  37. data/spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb +0 -3
  38. data/spec/lib/pact_broker/pacts/generate_sha_spec.rb +92 -0
  39. data/spec/lib/pact_broker/pacts/repository_spec.rb +47 -4
  40. data/spec/lib/pact_broker/pacts/sort_content_spec.rb +44 -0
  41. data/spec/lib/pact_broker/versions/repository_spec.rb +13 -5
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/support/foo-bar.json +34 -0
  44. data/spec/support/rspec_match_hash.rb +14 -17
  45. data/spec/support/test_data_builder.rb +5 -4
  46. metadata +26 -8
  47. data/lib/pact_broker/matrix/latest_row.rb +0 -10
  48. data/lib/pact_broker/pacts/sort_verifiable_content.rb +0 -41
  49. data/spec/lib/pact_broker/pacts/sort_verifiable_content_spec.rb +0 -25
@@ -7,6 +7,29 @@ module PactBroker
7
7
  class HeadRow < Row
8
8
  set_dataset(:materialized_head_matrix)
9
9
 
10
+ # one_to_one :latest_verification_for_consumer_version_tag,
11
+ # :class => "PactBroker::Verifications::LatestVerificationForConsumerVersionTag",
12
+ # primary_key: [:provider_id, :consumer_id, :consumer_version_tag_name], key: [:provider_id, :consumer_id, :consumer_version_tag_name]
13
+
14
+ # Loading the latest_verification_for_consumer_version_tag objects this way is quicker than
15
+ # doing it using an inbult relation with primary_key/key, if we are loading the relation for
16
+ # the entire HeadRow table
17
+ # Using the inbuilt relation puts constraints on the columns that are not necessary and slow
18
+ # the query down.
19
+ # This relation relies on consumer_version_tags already being loaded
20
+ one_to_one :latest_verification_for_consumer_version_tag, :class => "PactBroker::Verifications::LatestVerificationForConsumerVersionTag", primary_keys: [], key: [], :eager_loader=>(proc do |eo_opts|
21
+ tag_to_row = eo_opts[:rows].each_with_object({}) { | row, map | map[[row.provider_id, row.consumer_id, row.consumer_version_tag_name]] = row }
22
+ eo_opts[:rows].each{|row| row.associations[:latest_verification_for_consumer_version_tag] = nil}
23
+
24
+ # Need the all then the each to ensure the eager loading works
25
+ PactBroker::Verifications::LatestVerificationForConsumerVersionTag.each do | verification |
26
+ key = [verification.provider_id, verification.consumer_id, verification.consumer_version_tag_name]
27
+ if tag_to_row[key]
28
+ tag_to_row[key].associations[:latest_verification_for_consumer_version_tag] = verification
29
+ end
30
+ end
31
+ end)
32
+
10
33
  dataset_module do
11
34
  include PactBroker::Repositories::Helpers
12
35
  include PactBroker::Logging
@@ -1,6 +1,5 @@
1
1
  require 'pact_broker/repositories/helpers'
2
2
  require 'pact_broker/matrix/row'
3
- require 'pact_broker/matrix/latest_row'
4
3
  require 'pact_broker/matrix/head_row'
5
4
  require 'pact_broker/error'
6
5
 
@@ -2,9 +2,12 @@ require 'pact_broker/repositories/helpers'
2
2
  require 'pact_broker/webhooks/latest_triggered_webhook'
3
3
  require 'pact_broker/tags/tag_with_latest_flag'
4
4
  require 'pact_broker/logging'
5
+ require 'pact_broker/verifications/latest_verification_for_consumer_version_tag'
6
+ require 'pact_broker/verifications/latest_verification_for_consumer_and_provider'
5
7
 
6
8
  module PactBroker
7
9
  module Matrix
10
+
8
11
  class Row < Sequel::Model(:materialized_matrix)
9
12
 
10
13
  # Used when using table_print to output query results
@@ -15,6 +18,10 @@ module PactBroker
15
18
  associate(:one_to_many, :consumer_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :consumer_version_id, key: :version_id)
16
19
  associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :provider_version_id, key: :version_id)
17
20
 
21
+ many_to_one :latest_verification_for_consumer_and_provider,
22
+ :class => "PactBroker::Verifications::LatestVerificationForConsumerAndProvider",
23
+ primary_key: [:provider_id, :consumer_id], key: [:provider_id, :consumer_id]
24
+
18
25
  dataset_module do
19
26
  include PactBroker::Repositories::Helpers
20
27
  include PactBroker::Logging
@@ -111,20 +118,6 @@ module PactBroker
111
118
  end
112
119
  end
113
120
 
114
- # tags for which this pact publication is the latest of that tag
115
- # this is set in the code rather than the database
116
- def consumer_head_tag_names
117
- @consumer_head_tag_names ||= []
118
- end
119
-
120
- def consumer_head_tag_names= consumer_head_tag_names
121
- @consumer_head_tag_names = consumer_head_tag_names
122
- end
123
-
124
- # def latest_triggered_webhooks
125
- # @latest_triggered_webhooks ||= []
126
- # end
127
-
128
121
  def summary
129
122
  "#{consumer_name}#{consumer_version_number} #{provider_name}#{provider_version_number || '?'} (r#{pact_revision_number}n#{verification_number || '?'})"
130
123
  end
@@ -138,17 +131,18 @@ module PactBroker
138
131
  end
139
132
 
140
133
  def consumer_version
141
- @consumer_version ||= OpenStruct.new(number: consumer_version_number, id: consumer_version_id, pacticipant: consumer)
134
+ @consumer_version ||= OpenStruct.new(number: consumer_version_number, order: consumer_version_order, id: consumer_version_id, pacticipant: consumer)
142
135
  end
143
136
 
144
137
  def provider_version
145
138
  if provider_version_number
146
- @provider_version ||= OpenStruct.new(number: provider_version_number, id: provider_version_id, pacticipant: provider)
139
+ @provider_version ||= OpenStruct.new(number: provider_version_number, order: provider_version_order, id: provider_version_id, pacticipant: provider)
147
140
  end
148
141
  end
149
142
 
150
143
  def pact
151
- @pact ||= OpenStruct.new(consumer: consumer,
144
+ @pact ||= OpenStruct.new(
145
+ consumer: consumer,
152
146
  provider: provider,
153
147
  consumer_version: consumer_version,
154
148
  consumer_version_number: consumer_version_number,
@@ -158,7 +152,7 @@ module PactBroker
158
152
  )
159
153
  end
160
154
 
161
- def latest_verification
155
+ def verification
162
156
  if verification_executed_at
163
157
  @latest_verification ||= OpenStruct.new(
164
158
  id: verification_id,
@@ -167,10 +161,12 @@ module PactBroker
167
161
  execution_date: verification_executed_at,
168
162
  created_at: verification_executed_at,
169
163
  provider_version_number: provider_version_number,
164
+ provider_version_order: provider_version_order,
170
165
  build_url: verification_build_url,
171
166
  provider_version: provider_version,
172
167
  consumer_name: consumer_name,
173
- provider_name: provider_name
168
+ provider_name: provider_name,
169
+ pact_version_sha: pact_version_sha
174
170
  )
175
171
  end
176
172
  end
@@ -187,7 +183,6 @@ module PactBroker
187
183
  ]
188
184
 
189
185
  comparisons.find{|c| c != 0 } || 0
190
-
191
186
  end
192
187
 
193
188
  def compare_name_asc name1, name2
@@ -0,0 +1,66 @@
1
+ require 'pact_broker/pacts/parse'
2
+ require 'pact_broker/pacts/sort_content'
3
+
4
+ module PactBroker
5
+ module Pacts
6
+ class Content
7
+
8
+ def initialize pact_hash
9
+ @pact_hash = pact_hash
10
+ end
11
+
12
+ def self.from_json json_content
13
+ new(Parse.call(json_content))
14
+ end
15
+
16
+ def self.from_hash pact_hash
17
+ new(pact_hash)
18
+ end
19
+
20
+ def to_hash
21
+ pact_hash
22
+ end
23
+
24
+ def to_json
25
+ pact_hash.to_json
26
+ end
27
+
28
+ def sort
29
+ Content.from_hash(SortContent.call(pact_hash))
30
+ end
31
+
32
+ # Half thinking this belongs in GenerateSha
33
+ def content_that_affects_verification_results
34
+ if interactions || messages
35
+ cont = {}
36
+ cont['interactions'] = interactions if interactions
37
+ cont['messages'] = messages if messages
38
+ cont['pact_specification_version'] = pact_specification_version if pact_specification_version
39
+ cont
40
+ else
41
+ pact_hash
42
+ end
43
+ end
44
+
45
+ def messages
46
+ pact_hash.is_a?(Hash) ? pact_hash['messages'] : nil
47
+ end
48
+
49
+ def interactions
50
+ pact_hash.is_a?(Hash) ? pact_hash['interactions'] : nil
51
+ end
52
+
53
+ def pact_specification_version
54
+ maybe_pact_specification_version_1 = pact_hash['metadata']['pactSpecification']['version'] rescue nil
55
+ maybe_pact_specification_version_2 = pact_hash['metadata']['pact-specification']['version'] rescue nil
56
+ maybe_pact_specification_version_3 = pact_hash['metadata'] && pact_hash['metadata']['pactSpecificationVersion'] rescue nil
57
+ maybe_pact_specification_version_1 || maybe_pact_specification_version_2 || maybe_pact_specification_version_3
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :pact_hash
63
+
64
+ end
65
+ end
66
+ end
@@ -14,7 +14,6 @@ module PactBroker
14
14
  difference = diff(previous_pact_hash, pact_hash)
15
15
  Pact::Matchers::UnixDiffFormatter.call(difference, colour: false, include_explanation: false)
16
16
  end
17
-
18
17
  end
19
18
  end
20
19
  end
@@ -1,7 +1,8 @@
1
1
  require 'pact_broker/api/pact_broker_urls'
2
2
  require 'pact_broker/date_helper'
3
3
  require 'pact_broker/pacts/create_formatted_diff'
4
- require 'pact_broker/pacts/sort_verifiable_content'
4
+ require 'pact_broker/pacts/sort_content'
5
+ require 'pact_broker/pacts/parse'
5
6
  require 'pact_broker/repositories'
6
7
  require 'yaml'
7
8
 
@@ -110,7 +111,7 @@ module PactBroker
110
111
  if options[:raw]
111
112
  json_content
112
113
  else
113
- PactBroker::Pacts::SortVerifiableContent.call(json_content)
114
+ SortContent.call(Parse.call(json_content)).to_json
114
115
  end
115
116
  end
116
117
  end
@@ -0,0 +1,24 @@
1
+ require 'digest/sha1'
2
+ require 'pact_broker/configuration'
3
+ require 'pact_broker/pacts/sort_content'
4
+ require 'pact_broker/pacts/parse'
5
+ require 'pact_broker/pacts/content'
6
+
7
+ module PactBroker
8
+ module Pacts
9
+ class GenerateSha
10
+ def self.call json_content, options = {}
11
+ content_for_sha = if PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results
12
+ extract_verifiable_content_for_sha(json_content)
13
+ else
14
+ json_content
15
+ end
16
+ Digest::SHA1.hexdigest(content_for_sha)
17
+ end
18
+
19
+ def self.extract_verifiable_content_for_sha json_content
20
+ Content.from_json(json_content).sort.content_that_affects_verification_results.to_json
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require 'pact_broker/json'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ class Parse
6
+ def self.call(json)
7
+ JSON.parse(json, PACT_PARSING_OPTIONS)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,7 +1,7 @@
1
- require 'digest/sha1'
2
1
  require 'sequel'
3
2
  require 'ostruct'
4
3
  require 'pact_broker/logging'
4
+ require 'pact_broker/pacts/generate_sha'
5
5
  require 'pact_broker/pacts/pact_publication'
6
6
  require 'pact_broker/pacts/all_pact_publications'
7
7
  require 'pact_broker/pacts/latest_pact_publications_by_consumer_version'
@@ -9,6 +9,7 @@ require 'pact_broker/pacts/latest_pact_publications'
9
9
  require 'pact_broker/pacts/latest_tagged_pact_publications'
10
10
  require 'pact/shared/json_differ'
11
11
  require 'pact_broker/domain'
12
+ require 'pact_broker/pacts/parse'
12
13
 
13
14
  module PactBroker
14
15
  module Pacts
@@ -197,16 +198,15 @@ module PactBroker
197
198
  end
198
199
 
199
200
  def find_or_create_pact_version consumer_id, provider_id, json_content
200
- sha = Digest::SHA1.hexdigest(json_content)
201
+ sha = PactBroker.configuration.sha_generator.call(json_content)
201
202
  PactVersion.find(sha: sha, consumer_id: consumer_id, provider_id: provider_id) || create_pact_version(consumer_id, provider_id, sha, json_content)
202
203
  end
203
204
 
204
205
  def create_pact_version consumer_id, provider_id, sha, json_content
205
- PactBroker.logger.debug("Creating new PactVersion for sha #{sha}")
206
+ logger.debug("Creating new pact version for sha #{sha}")
206
207
  pact_version = PactVersion.new(consumer_id: consumer_id, provider_id: provider_id, sha: sha, content: json_content)
207
208
  pact_version.save
208
209
  end
209
-
210
210
  end
211
211
  end
212
212
  end
@@ -102,7 +102,8 @@ module PactBroker
102
102
  private
103
103
 
104
104
  def update_pact params, existing_pact
105
- logger.info "Updating existing pact version with params #{params}"
105
+ logger.info "Updating existing pact publication with params #{params.reject{ |k, v| k == :json_content}}"
106
+ logger.debug "Content #{params[:json_content]}"
106
107
  updated_pact = pact_repository.update existing_pact.id, params
107
108
 
108
109
  if existing_pact.json_content != updated_pact.json_content
@@ -113,7 +114,8 @@ module PactBroker
113
114
  end
114
115
 
115
116
  def create_pact params, version, provider
116
- logger.info "Creating new pact version with params #{params}"
117
+ logger.info "Creating new pact publication with params #{params.reject{ |k, v| k == :json_content}}"
118
+ logger.debug "Content #{params[:json_content]}"
117
119
  pact = pact_repository.create json_content: params[:json_content], version_id: version.id, provider_id: provider.id, consumer_id: version.pacticipant_id
118
120
  trigger_webhooks pact
119
121
  pact
@@ -0,0 +1,54 @@
1
+ require 'pact_broker/json'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ class SortContent
6
+ def self.call pact_hash
7
+ key = verifiable_content_key_for(pact_hash)
8
+
9
+ if key
10
+ content = pact_hash[key]
11
+ sorted_pact_hash = order_object(pact_hash)
12
+ sorted_pact_hash[key] = order_verifiable_content(content)
13
+ sorted_pact_hash
14
+ else
15
+ pact_hash
16
+ end
17
+ end
18
+
19
+ def self.verifiable_content_key_for pact_hash
20
+ if pact_hash['interactions']
21
+ 'interactions'
22
+ elsif pact_hash['messages']
23
+ 'messages'
24
+ else
25
+ nil
26
+ end
27
+ end
28
+
29
+
30
+ def self.order_verifiable_content array
31
+ array_with_ordered_hashes = order_object(array)
32
+ array_with_ordered_hashes.sort{|a, b| a.to_json <=> b.to_json }
33
+ end
34
+
35
+ def self.order_object thing
36
+ case thing
37
+ when Hash then order_hash(thing)
38
+ when Array then order_child_array(thing)
39
+ else thing
40
+ end
41
+ end
42
+
43
+ def self.order_child_array array
44
+ array.collect{|thing| order_object(thing) }
45
+ end
46
+
47
+ def self.order_hash hash
48
+ hash.keys.sort.each_with_object({}) do | key, new_hash |
49
+ new_hash[key] = order_object(hash[key])
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,26 @@
1
+ require 'pact_broker/domain/verification'
2
+
3
+ module PactBroker
4
+ module Verifications
5
+
6
+ include PactBroker::Repositories::Helpers
7
+
8
+ class LatestVerificationForConsumerAndProvider < PactBroker::Domain::Verification
9
+ set_dataset(:latest_verifications_for_consumer_and_provider)
10
+
11
+ # Don't need to load the pact_version as we do in the superclass,
12
+ # as pact_version_sha is included in the view for convenience
13
+ def pact_version_sha
14
+ values[:pact_version_sha]
15
+ end
16
+
17
+ def provider_version_number
18
+ values[:provider_version_number]
19
+ end
20
+
21
+ def provider_version_order
22
+ values[:provider_version_order]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ require 'pact_broker/domain/verification'
2
+
3
+ module PactBroker
4
+ module Verifications
5
+ class LatestVerificationForConsumerVersionTag < PactBroker::Domain::Verification
6
+ set_dataset(:latest_verifications_for_consumer_version_tags)
7
+
8
+ # Don't need to load the pact_version as we do in the superclass,
9
+ # as pact_version_sha is included in the view for convenience
10
+ def pact_version_sha
11
+ values[:pact_version_sha]
12
+ end
13
+
14
+ def provider_version_number
15
+ values[:provider_version_number]
16
+ end
17
+
18
+ def provider_version_order
19
+ values[:provider_version_order]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.19.1'
2
+ VERSION = '2.19.2'
3
3
  end
@@ -51,10 +51,19 @@ module PactBroker
51
51
  .single_record
52
52
  end
53
53
 
54
+ # There may be a race condition if two simultaneous requests come in to create the same version
54
55
  def create args
55
- PactBroker.logger.info "Creating version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
56
- version = PactBroker::Domain::Version.new(number: args[:number], pacticipant_id: args[:pacticipant_id]).save
57
- PactBroker::Domain::Version.find(id: version.id) # Need to reload with populated order
56
+ PactBroker.logger.info "Upserting version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
57
+ version_params = {
58
+ number: args[:number],
59
+ pacticipant_id: args[:pacticipant_id],
60
+ created_at: Sequel.datetime_class.now,
61
+ updated_at: Sequel.datetime_class.now
62
+ }
63
+ id = PactBroker::Domain::Version.db[:versions].insert_ignore.insert(version_params)
64
+ version = PactBroker::Domain::Version.find(number: args[:number], pacticipant_id: args[:pacticipant_id])
65
+ PactBroker::Domain::OrderVersions.(version)
66
+ version.refresh # reload with the populated order
58
67
  end
59
68
 
60
69
  def find_by_pacticipant_id_and_number_or_create pacticipant_id, number