pact_broker 2.99.0 → 2.100.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -0
  3. data/lib/pact_broker/api/decorators/reason_decorator.rb +1 -15
  4. data/lib/pact_broker/api/resources/deployed_version.rb +8 -2
  5. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -5
  6. data/lib/pact_broker/api/resources/pacticipant_resource_methods.rb +0 -1
  7. data/lib/pact_broker/api/resources/publish_contracts.rb +1 -1
  8. data/lib/pact_broker/api/resources/released_version.rb +8 -2
  9. data/lib/pact_broker/api/resources/released_versions_for_version_and_environment.rb +1 -1
  10. data/lib/pact_broker/contracts/contract_to_publish.rb +4 -0
  11. data/lib/pact_broker/contracts/contracts_to_publish.rb +4 -0
  12. data/lib/pact_broker/contracts/service.rb +23 -5
  13. data/lib/pact_broker/domain/tag.rb +8 -32
  14. data/lib/pact_broker/domain/version.rb +18 -10
  15. data/lib/pact_broker/locale/en.yml +7 -4
  16. data/lib/pact_broker/matrix/deployment_status_summary.rb +28 -19
  17. data/lib/pact_broker/matrix/parse_query.rb +5 -0
  18. data/lib/pact_broker/matrix/quick_row.rb +5 -2
  19. data/lib/pact_broker/matrix/repository.rb +3 -3
  20. data/lib/pact_broker/matrix/resolved_selector.rb +47 -10
  21. data/lib/pact_broker/matrix/service.rb +4 -2
  22. data/lib/pact_broker/matrix/unresolved_selector.rb +14 -2
  23. data/lib/pact_broker/messages.rb +0 -15
  24. data/lib/pact_broker/pacticipants/service.rb +11 -1
  25. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +4 -2
  26. data/lib/pact_broker/repositories/helpers.rb +13 -0
  27. data/lib/pact_broker/test/http_test_data_builder.rb +14 -0
  28. data/lib/pact_broker/ui/views/matrix/show.haml +5 -2
  29. data/lib/pact_broker/version.rb +1 -1
  30. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: edd8be1bf717f3b98618f1f585146efa701f4833a9c2d8ba05eb395278bd435c
4
- data.tar.gz: 36c07bdd448edd40e623c0976ec65df4c2512ddf73ce400512e27d08f7a086d8
3
+ metadata.gz: f22862d2054c07b06fb947f8a1f710dab261ae6ac1207e6a15adebb07dc488db
4
+ data.tar.gz: 5cc63160ad026673f194af54237080aeb6eb8ab4f9ac93cbe5cd71b63727d60e
5
5
  SHA512:
6
- metadata.gz: 023ae6605034518c73da217fff6be8c6888bd7d56abcbf11895f18ec3eab9e32775ec574381473e626e1bca75bc3b132fd30fd755157e980d3e16dea9c125f4f
7
- data.tar.gz: 67d5dcb38a78a5e590ec961826951e32c4e391ad13eea43fbf57faa48de2c20212397cdb116b93c975ecdb576e7790112e6db16b43ba5c866af4c074c0370cd3
6
+ metadata.gz: 8b6c0cefc1e4847ed53627f0b2aae7cfd5be46e351ce1969f21b80f5f8899990cca3f612d9ab14a82d58c0521b91fb5f5a820f4c7d9e72f17fbc391f1583daff
7
+ data.tar.gz: 4c1c3fa1a8e983718f1f841c8b17088678c6f4ada22c36b3ededb71a46c2bf004712c4716a8af33c17e068c88b88a6b69f219a286549841c20fa8c79ae41a214
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ <a name="v2.100.0"></a>
2
+ ### v2.100.0 (2022-05-20)
3
+
4
+ #### Features
5
+
6
+ * check for potential duplicate pacticipants in publish contracts endpoint (#558) ([ed714f03](/../../commit/ed714f03))
7
+ * add support for "can i merge" matrix query ([bb108ed2](/../../commit/bb108ed2))
8
+ * remove inefficient skynet query for tags ([bdc2599c](/../../commit/bdc2599c))
9
+ * update text for matrix version description when no version is in an enviroment ([3eb5581d](/../../commit/3eb5581d))
10
+
11
+ * **pacts for verification**
12
+ * update wip and pending descriptions for wip pacts ([b06d4477](/../../commit/b06d4477))
13
+
14
+ * **matrix**
15
+ * add support for selectors specified by branch and environment name when reporting that a version does not exist ([07ff8044](/../../commit/07ff8044))
16
+
17
+ #### Bug Fixes
18
+
19
+ * **matrix**
20
+ * identify the correct failed version when a selector resolves to multiple versions ([11e7dc1c](/../../commit/11e7dc1c))
21
+
1
22
  <a name="v2.99.0"></a>
2
23
  ### v2.99.0 (2022-05-13)
3
24
 
@@ -30,7 +30,7 @@ module PactBroker
30
30
  when PactBroker::Matrix::PactNotVerifiedByRequiredProviderVersion
31
31
  "There is no verified pact between #{reason.consumer_selector.description} and #{reason.provider_selector.description}"
32
32
  when PactBroker::Matrix::SpecifiedVersionDoesNotExist
33
- version_does_not_exist_description(reason.selector)
33
+ reason.selector.version_does_not_exist_description
34
34
  when PactBroker::Matrix::VerificationFailed
35
35
  "The verification for the pact between #{reason.consumer_selector.description} and #{reason.provider_selector.description} failed"
36
36
  when PactBroker::Matrix::NoDependenciesMissing
@@ -53,20 +53,6 @@ module PactBroker
53
53
  end
54
54
  end
55
55
 
56
- def version_does_not_exist_description selector
57
- if selector.version_does_not_exist?
58
- if selector.tag
59
- "No version with tag #{selector.tag} exists for #{selector.pacticipant_name}"
60
- elsif selector.pacticipant_version_number
61
- "No pacts or verifications have been published for version #{selector.pacticipant_version_number} of #{selector.pacticipant_name}"
62
- else
63
- "No pacts or verifications have been published for #{selector.pacticipant_name}"
64
- end
65
- else
66
- ""
67
- end
68
- end
69
-
70
56
  # TODO move this somewhere else
71
57
  def interaction_description(interaction)
72
58
  if interaction["providerState"] && interaction["providerState"] != ""
@@ -58,11 +58,17 @@ module PactBroker
58
58
  end
59
59
 
60
60
  def policy_name
61
- :'versions::version'
61
+ :'versions::deployed_version'
62
+ end
63
+
64
+ def policy_record_context
65
+ {
66
+ pacticipant: deployed_version&.pacticipant
67
+ }
62
68
  end
63
69
 
64
70
  def policy_record
65
- deployed_version&.version
71
+ deployed_version&.environment
66
72
  end
67
73
 
68
74
  private
@@ -40,11 +40,7 @@ module PactBroker
40
40
  end
41
41
 
42
42
  def policy_name
43
- :'versions::deployed_version'
44
- end
45
-
46
- def policy_record
47
- environment
43
+ :'versions::deployed_versions'
48
44
  end
49
45
 
50
46
  private
@@ -1,7 +1,6 @@
1
1
  module PactBroker
2
2
  module Api
3
3
  module Resources
4
-
5
4
  module PacticipantResourceMethods
6
5
 
7
6
  def potential_duplicate_pacticipants? pacticipant_names
@@ -100,7 +100,7 @@ module PactBroker
100
100
  end
101
101
 
102
102
  def conflict_notices
103
- @conflict_notices ||= contract_service.conflict_notices(parsed_contracts)
103
+ @conflict_notices ||= contract_service.conflict_notices(parsed_contracts, base_url: base_url)
104
104
  end
105
105
 
106
106
  def base64_decode(content)
@@ -48,11 +48,17 @@ module PactBroker
48
48
  end
49
49
 
50
50
  def policy_name
51
- :'versions::version'
51
+ :'versions::released_version'
52
+ end
53
+
54
+ def policy_record_context
55
+ {
56
+ pacticipant: released_version&.pacticipant
57
+ }
52
58
  end
53
59
 
54
60
  def policy_record
55
- released_version&.version
61
+ released_version&.environment
56
62
  end
57
63
 
58
64
  private
@@ -45,7 +45,7 @@ module PactBroker
45
45
  end
46
46
 
47
47
  def policy_name
48
- :'versions::released_version'
48
+ :'versions::released_versions'
49
49
  end
50
50
 
51
51
  def policy_record
@@ -14,6 +14,10 @@ module PactBroker
14
14
  def merge?
15
15
  on_conflict == "merge"
16
16
  end
17
+
18
+ def pacticipant_names
19
+ [consumer_name, provider_name]
20
+ end
17
21
  end
18
22
  end
19
23
  end
@@ -6,6 +6,10 @@ module PactBroker
6
6
  new(pacticipant_name, pacticipant_version_number, tags, branch, build_url, contracts)
7
7
  end
8
8
  # rubocop: enable Metrics/ParameterLists
9
+
10
+ def pacticipant_names
11
+ contracts.flat_map(&:pacticipant_names).uniq
12
+ end
9
13
  end
10
14
  end
11
15
  end
@@ -43,19 +43,26 @@ module PactBroker
43
43
  )
44
44
  end
45
45
 
46
- def conflict_notices(parsed_contracts)
46
+ def conflict_notices(parsed_contracts, base_url: )
47
47
  notices = []
48
+ add_pact_conflict_notices(notices, parsed_contracts)
49
+ add_pacticipant_conflict_notices(notices, parsed_contracts, base_url)
50
+ notices
51
+ end
52
+
53
+ def add_pact_conflict_notices(notices, parsed_contracts)
48
54
  parsed_contracts.contracts.collect do | contract_to_publish |
49
55
  pact_params = create_pact_params(parsed_contracts, contract_to_publish)
50
56
  existing_pact = pact_service.find_pact(pact_params)
51
57
  if existing_pact && pact_service.disallowed_modification?(existing_pact, contract_to_publish.decoded_content)
52
- add_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_pact.json_content, contract_to_publish.decoded_content)
58
+ add_pact_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_pact.json_content, contract_to_publish.decoded_content)
53
59
  end
54
60
  end
55
- notices
56
61
  end
57
62
 
58
- def add_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_json_content, new_json_content)
63
+ private :add_pact_conflict_notices
64
+
65
+ def add_pact_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_json_content, new_json_content)
59
66
  message_params = {
60
67
  consumer_name: contract_to_publish.provider_name,
61
68
  consumer_version_number: parsed_contracts.pacticipant_version_number,
@@ -65,7 +72,18 @@ module PactBroker
65
72
  notices << Notice.info(PactBroker::Pacts::CreateFormattedDiff.call(new_json_content, existing_json_content))
66
73
  end
67
74
 
68
- private :add_conflict_notice
75
+ private :add_pact_conflict_notice
76
+
77
+ def add_pacticipant_conflict_notices(notices, parsed_contracts, base_url)
78
+ if PactBroker.configuration.check_for_potential_duplicate_pacticipant_names
79
+ duplicate_pacticipant_messages = pacticipant_service.messages_for_potential_duplicate_pacticipants(parsed_contracts.pacticipant_names, base_url)
80
+ duplicate_pacticipant_messages.each do | message_text |
81
+ notices << Notice.error(message_text)
82
+ end
83
+ end
84
+ end
85
+
86
+ private :add_pacticipant_conflict_notices
69
87
 
70
88
  def create_version(parsed_contracts)
71
89
  version_params = {
@@ -39,53 +39,28 @@ module PactBroker
39
39
  end
40
40
 
41
41
  def latest_tags
42
- self_join = {
43
- Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
44
- Sequel[:tags][:name] => Sequel[:tags_2][:name]
45
- }
46
-
47
- PactBroker::Domain::Tag
42
+ Tag
48
43
  .select_all_qualified
49
- .left_join(:tags, self_join, { table_alias: :tags_2 }) do
50
- Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
51
- end
52
- .where(Sequel[:tags_2][:name] => nil)
44
+ .max_group_by(:version_order, [:pacticipant_id, :name])
53
45
  end
54
46
 
55
47
  # Does NOT care about whether or not there is a pact publication
56
48
  # for the version
57
49
  def latest_tags_for_pacticipant_ids(pacticipant_ids)
58
- self_join = {
59
- Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
60
- Sequel[:tags][:name] => Sequel[:tags_2][:name],
61
- Sequel[:tags_2][:pacticipant_id] => pacticipant_ids,
62
- }
63
-
64
50
  Tag
65
51
  .select_all_qualified
66
- .left_join(:tags, self_join, { table_alias: :tags_2 }) do
67
- Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
52
+ .max_group_by(:version_order, [:pacticipant_id, :name]) do | ds |
53
+ ds.where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
68
54
  end
69
- .where(Sequel[:tags_2][:name] => nil)
70
- .where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
71
55
  end
72
56
 
73
57
  def latest_tags_for_pacticipant_ids_and_tag_names(pacticipant_ids, tag_names)
74
- self_join = {
75
- Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
76
- Sequel[:tags][:name] => Sequel[:tags_2][:name],
77
- Sequel[:tags_2][:pacticipant_id] => pacticipant_ids,
78
- Sequel[:tags_2][:name] => tag_names
79
- }
80
-
81
58
  Tag
82
59
  .select_all_qualified
83
- .left_join(:tags, self_join, { table_alias: :tags_2 }) do
84
- Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
60
+ .max_group_by(:version_order, [:pacticipant_id, :name]) do | ds |
61
+ ds.where(Sequel[:tags][:name] => tag_names)
62
+ .where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
85
63
  end
86
- .where(Sequel[:tags_2][:name] => nil)
87
- .where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
88
- .where(Sequel[:tags][:name] => tag_names)
89
64
  end
90
65
 
91
66
  # ignores tags that don't have a pact publication
@@ -110,6 +85,7 @@ module PactBroker
110
85
  .join(lp, versions_pact_publications_join)
111
86
  end
112
87
 
88
+ # TODO convert this to use max and join
113
89
  def head_tags_for_pact_publication(pact_publication)
114
90
  Tag.where(version_id: pact_publication.consumer_version_id).all.select do | tag |
115
91
  tag_pp_join = {
@@ -141,10 +141,12 @@ module PactBroker
141
141
  where(id: PactBroker::Versions::BranchVersion.select(:version_id))
142
142
  else
143
143
  matching_branch_ids = PactBroker::Versions::Branch.select(:id).where(name: branch_name)
144
- matching_branch_version_ids = PactBroker::Versions::BranchVersion
145
- .select(:version_id)
144
+ branch_version_ids = PactBroker::Versions::BranchVersion
145
+ .select(:version_id, :branch_name)
146
146
  .where(branch_id: matching_branch_ids)
147
- where(id: matching_branch_version_ids)
147
+ select_append(:branch_name)
148
+ .join(branch_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bv][:version_id] }, { table_alias: :bv})
149
+
148
150
  end
149
151
  end
150
152
 
@@ -152,18 +154,22 @@ module PactBroker
152
154
  if branch_name == true
153
155
  where(id: PactBroker::Versions::BranchHead.select(:version_id))
154
156
  else
155
- where(id: PactBroker::Versions::BranchHead.select(:version_id).where(branch_name: branch_name))
157
+ branch_heads = PactBroker::Versions::BranchHead.select(:version_id, :branch_name).where(branch_name: branch_name)
158
+ select_append(:branch_name)
159
+ .join(branch_heads, { Sequel[first_source_alias][:id] => Sequel[:bh][:version_id] }, { table_alias: :bh })
156
160
  end
157
161
  end
158
162
 
159
163
 
160
164
  def for_main_branches
161
- matching_branch_version_ids = PactBroker::Versions::BranchVersion
162
- .select(:version_id)
165
+ branch_version_ids = PactBroker::Versions::BranchVersion
166
+ .select(:version_id, :branch_name)
163
167
  .join(:pacticipants, { Sequel[:branch_versions][:pacticipant_id] => Sequel[:pacticipants][:id] })
164
168
  .join(:branches, { Sequel[:branches][:id] => Sequel[:branch_versions][:branch_id], Sequel[:branches][:name] => Sequel[:pacticipants][:main_branch] })
165
169
 
166
- where(id: matching_branch_version_ids)
170
+ select_append(Sequel[:bv][:branch_name])
171
+ .join(branch_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bv][:version_id] }, table_alias: :bv)
172
+
167
173
  end
168
174
 
169
175
  def latest_for_main_branches
@@ -171,11 +177,13 @@ module PactBroker
171
177
  Sequel[:branch_heads][:pacticipant_id] => Sequel[:pacticipants][:id],
172
178
  Sequel[:branch_heads][:branch_name] => Sequel[:pacticipants][:main_branch]
173
179
  }
174
- matching_branch_version_ids = PactBroker::Versions::BranchHead
175
- .select(:version_id)
180
+ branch_head_version_ids = PactBroker::Versions::BranchHead
181
+ .select(:version_id, :branch_name)
176
182
  .join(:pacticipants, pacticipants_join)
177
183
 
178
- where(id: matching_branch_version_ids)
184
+ select_append(Sequel[:bh][:branch_name])
185
+ .join(branch_head_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bh][:version_id] }, table_alias: :bh)
186
+
179
187
  end
180
188
 
181
189
  def where_number(number)
@@ -87,6 +87,7 @@ en:
87
87
  environment_name_must_be_unique: Another environment with name '%{name}' already exists.
88
88
  cannot_specify_tag_and_environment: Cannot specify both a 'to' tag and an environment.
89
89
  cannot_specify_latest_and_environment: Cannot specify both latest=true and an environment.
90
+ cannot_specify_more_than_one_destination_identifier: Cannot specify more than one of tag, environment and mainBranch.
90
91
  environment_with_name_not_found: "Environment with name '%{name}' does not exist"
91
92
  cannot_modify_version_branch: "The branch for a pacticipant version cannot be changed once set (currently '%{old_branch}', proposed value '%{new_branch}'). Your pact publication/verification publication configurations may be in conflict with each other if you are seeing this error. If the current branch value is incorrect, you must delete the pacticipant version resource at %{version_url} and publish the pacts/verification results again with a consistent branch name."
92
93
  invalid_content_for_content_type: "The content could not be parsed as %{content_type}"
@@ -95,12 +96,14 @@ en:
95
96
  invalid_limit: The limit must be 1 or greater.
96
97
  duplicate_pacticipant: |
97
98
  This is the first time a pact has been published for "%{new_name}".
98
- The name "%{new_name}" is very similar to the following existing consumers/providers:
99
+ The name "%{new_name}" is very similar to the following existing pacticipants:
99
100
  %{existing_names}
100
101
  If you meant to specify one of the above names, please correct the pact configuration, and re-publish the pact.
101
- If the pact is intended to be for a new consumer or provider, please manually create "%{new_name}" using the following command, and then re-publish the pact:
102
- $ curl -v -XPOST -H "Content-Type: application/json" -d "{\"name\": \"%{new_name}\"}" %{create_pacticipant_url}
103
- If the pact broker requires basic authentication, add '-u <username:password>' to the command.
102
+ If the pact is intended to be for a new consumer or provider, please manually create "%{new_name}" using the following command from the Pact Broker CLI, and then re-publish the pact:
103
+
104
+ pact-broker create-or-update-pacticipant --name "%{new_name}" --broker-base-url %{base_url}
105
+
106
+ For information on the Pact Broker CLI see https://docs.pact.io/go/pbc-cli
104
107
  To disable this check, set `check_for_potential_duplicate_pacticipant_names` to false in the configuration.
105
108
  new_line_in_url_path: URL path cannot contain a new line character.
106
109
  tab_in_url_path: URL path cannot contain a tab character.
@@ -62,7 +62,7 @@ module PactBroker
62
62
  def bad_practice_warnings
63
63
  warnings = []
64
64
 
65
- if no_to_tag_or_environment_specified?
65
+ if resolved_selectors.count(&:specified?) == 1 && no_to_tag_or_branch_or_environment_specified?
66
66
  warnings << NoEnvironmentSpecified.new
67
67
  end
68
68
 
@@ -91,8 +91,8 @@ module PactBroker
91
91
  .any?
92
92
  end
93
93
 
94
- def no_to_tag_or_environment_specified?
95
- !(query_results.options[:tag] || query_results.options[:environment_name])
94
+ def no_to_tag_or_branch_or_environment_specified?
95
+ !(query_results.options[:tag] || query_results.options[:environment_name] || query_results.options[:main_branch])
96
96
  end
97
97
 
98
98
  def considered_specified_selectors_that_do_not_exist
@@ -170,16 +170,6 @@ module PactBroker
170
170
  end.flatten
171
171
  end
172
172
 
173
- def selectors_without_a_version_for(integration)
174
- selectors_with_non_existing_versions.select do | selector |
175
- integration.involves_pacticipant_with_name?(selector.pacticipant_name)
176
- end
177
- end
178
-
179
- def selectors_with_non_existing_versions
180
- @selectors_with_non_existing_versions ||= resolved_selectors.select(&:latest_tagged_version_that_does_not_exist?)
181
- end
182
-
183
173
  def missing_specified_version_reasons(selectors)
184
174
  selectors.collect(&:version_does_not_exist_description)
185
175
  end
@@ -192,13 +182,32 @@ module PactBroker
192
182
  PactNotEverVerifiedByProvider.new(*selectors_for(row))
193
183
  end
194
184
 
195
- def selector_for(pacticipant_name)
196
- resolved_selectors.find{ | s| s.pacticipant_name == pacticipant_name } ||
197
- dummy_selectors.find{ | s| s.pacticipant_name == pacticipant_name }
185
+ # Find the resolved version selector that caused the matrix row with the
186
+ # specified pacticipant name and version number to be returned in the query.
187
+ #
188
+ # @return [PactBroker::Matrix::ResolvedSelector]
189
+ def selector_for(pacticipant_name, pacticipant_version_number)
190
+ found = resolved_selectors.select{ | s| s.pacticipant_name == pacticipant_name }
191
+
192
+ if found.size == 1
193
+ found.first
194
+ elsif pacticipant_version_number
195
+ found.find{ |s| s.pacticipant_version_number == pacticipant_version_number } || dummy_selector_for(pacticipant_name)
196
+ else
197
+ dummy_selector_for(pacticipant_name)
198
+ end
199
+ end
200
+
201
+ def selectors_for(row_or_integration)
202
+ if row_or_integration.respond_to?(:consumer_version_number)
203
+ [selector_for(row_or_integration.consumer_name, row_or_integration.consumer_version_number), selector_for(row_or_integration.provider_name, row_or_integration.provider_version_number)]
204
+ else
205
+ [selector_for(row_or_integration.consumer_name, nil), selector_for(row_or_integration.provider_name, nil)]
206
+ end
198
207
  end
199
208
 
200
- def selectors_for(row)
201
- [selector_for(row.consumer_name), selector_for(row.provider_name)]
209
+ def dummy_selector_for(pacticipant_name)
210
+ dummy_selectors.find{ | s| s.pacticipant_name == pacticipant_name }
202
211
  end
203
212
 
204
213
  # When the user has not specified a version of the provider (eg no 'latest' and/or 'tag', which means 'all versions')
@@ -232,7 +241,7 @@ module PactBroker
232
241
  considered_rows.select(&:success).collect do | row |
233
242
  begin
234
243
  if row.verification.interactions_missing_test_results.any? && !row.verification.all_interactions_missing_test_results?
235
- InteractionsMissingVerifications.new(selector_for(row.consumer_name), selector_for(row.provider_name), row.verification.interactions_missing_test_results)
244
+ InteractionsMissingVerifications.new(selector_for(row.consumer_name, row.consumer_version_number), selector_for(row.provider_name, row.provider_version_number), row.verification.interactions_missing_test_results)
236
245
  end
237
246
  rescue StandardError => e
238
247
  logger.warn("Error determining if there were missing interaction verifications", e)
@@ -48,6 +48,10 @@ module PactBroker
48
48
  options[:environment_name] = params["environment"]
49
49
  end
50
50
 
51
+ if params.key?("mainBranch") && params["mainBranch"] != ""
52
+ options[:main_branch] = params["mainBranch"] == "true"
53
+ end
54
+
51
55
  if params["ignore"].is_a?(Array)
52
56
  options[:ignore_selectors] = params["ignore"].collect{ |i| parse_selector(i) }
53
57
  else
@@ -65,6 +69,7 @@ module PactBroker
65
69
  p.branch = i["branch"] if i["branch"] && i["branch"] != ""
66
70
  p.tag = i["tag"] if i["tag"] && i["tag"] != ""
67
71
  p.environment_name = i["environment"] if i["environment"] && i["environment"] != ""
72
+ p.main_branch = true if i["mainBranch"] && i["mainBranch"] == "true"
68
73
  p
69
74
  end
70
75
  # rubocop: enable Metrics/CyclomaticComplexity
@@ -72,6 +72,8 @@ module PactBroker
72
72
  SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS
73
73
  SELECT_PACTICIPANT_IDS_ARGS = [:select_pacticipant_ids, Sequel[:p][:consumer_id], Sequel[:p][:provider_id]]
74
74
 
75
+ EAGER_LOADED_RELATIONSHIPS_FOR_VERSION = { current_deployed_versions: :environment, current_supported_released_versions: :environment, branch_versions: [:branch_head, :version, branch: :pacticipant] }
76
+
75
77
  associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
76
78
  associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
77
79
  associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
@@ -141,6 +143,7 @@ module PactBroker
141
143
  order(Sequel.desc(:consumer_version_created_at), Sequel.desc(:pact_order))
142
144
  end
143
145
 
146
+ # eager load tags?
144
147
  def eager_all_the_things
145
148
  eager(
146
149
  :consumer,
@@ -148,8 +151,8 @@ module PactBroker
148
151
  :verification,
149
152
  :pact_publication,
150
153
  :pact_version,
151
- consumer_version: { current_deployed_versions: :environment, current_supported_released_versions: :environment, branch_versions: [:branch_head, :version, branch: :pacticipant] },
152
- provider_version: { current_deployed_versions: :environment, current_supported_released_versions: :environment, branch_versions: [:branch_head, :version, branch: :pacticipant] },
154
+ consumer_version: EAGER_LOADED_RELATIONSHIPS_FOR_VERSION,
155
+ provider_version: EAGER_LOADED_RELATIONSHIPS_FOR_VERSION,
153
156
  consumer_version_tags: { version: :pacticipant },
154
157
  provider_version_tags: { version: :pacticipant }
155
158
  )
@@ -262,10 +262,9 @@ module PactBroker
262
262
  end
263
263
 
264
264
  # The user has specified --to TAG or --to-environment ENVIRONMENT in the CLI
265
- # (or nothing, which to defaults to "with the latest version of the other integrated applications")
266
- # The branch isn't implemented in the CLI yet (March 2022), but the API supports it.
265
+ # (or nothing, which to defaults to latest=true - "with the latest version of the other integrated applications")
267
266
  def infer_selectors_for_integrations?(options)
268
- options[:latest] || options[:tag] || options[:branch] || options[:environment_name]
267
+ options[:latest] || options[:tag] || options[:branch] || options[:environment_name] || options[:main_branch]
269
268
  end
270
269
 
271
270
  # When only one selector is specified, (eg. checking to see if Foo version 2 can be deployed to prod),
@@ -282,6 +281,7 @@ module PactBroker
282
281
  selector = UnresolvedSelector.new(pacticipant_name: pacticipant_name)
283
282
  selector.tag = options[:tag] if options[:tag]
284
283
  selector.branch = options[:branch] if options[:branch]
284
+ selector.main_branch = options[:main_branch] if options[:main_branch]
285
285
  selector.latest = options[:latest] if options[:latest]
286
286
  selector.environment_name = options[:environment_name] if options[:environment_name]
287
287
  selector
@@ -45,7 +45,8 @@ module PactBroker
45
45
  pacticipant_version_number: version.number,
46
46
  latest: original_selector[:latest],
47
47
  tag: original_selector[:tag],
48
- branch: original_selector[:branch],
48
+ branch: original_selector[:branch] || (original_selector[:main_branch] ? version&.values[:branch_name] : nil),
49
+ main_branch: original_selector[:main_branch],
49
50
  environment_name: original_selector[:environment_name],
50
51
  type: type,
51
52
  ignore: ignore,
@@ -64,6 +65,7 @@ module PactBroker
64
65
  latest: original_selector[:latest],
65
66
  tag: original_selector[:tag],
66
67
  branch: original_selector[:branch],
68
+ main_branch: original_selector[:main_branch],
67
69
  environment_name: original_selector[:environment_name],
68
70
  type: type,
69
71
  ignore: ignore,
@@ -99,10 +101,16 @@ module PactBroker
99
101
  self[:tag]
100
102
  end
101
103
 
104
+ # @return [String] the name of the branch
102
105
  def branch
103
106
  self[:branch]
104
107
  end
105
108
 
109
+ # @return [Boolean]
110
+ def main_branch?
111
+ self[:main_branch]
112
+ end
113
+
106
114
  def environment_name
107
115
  self[:environment_name]
108
116
  end
@@ -116,7 +124,7 @@ module PactBroker
116
124
  end
117
125
 
118
126
  def only_pacticipant_name_specified?
119
- !!pacticipant_name && !branch && !tag && !latest? && !pacticipant_version_number
127
+ !!pacticipant_name && !branch && !tag && !latest? && !pacticipant_version_number && !main_branch?
120
128
  end
121
129
 
122
130
  def latest_tagged?
@@ -127,6 +135,10 @@ module PactBroker
127
135
  latest? && branch
128
136
  end
129
137
 
138
+ def latest_from_main_branch?
139
+ latest? && main_branch?
140
+ end
141
+
130
142
  def pacticipant_or_version_does_not_exist?
131
143
  pacticipant_does_not_exist? || version_does_not_exist?
132
144
  end
@@ -139,10 +151,6 @@ module PactBroker
139
151
  !version_exists?
140
152
  end
141
153
 
142
- def latest_tagged_version_that_does_not_exist?
143
- version_does_not_exist? && latest_tagged?
144
- end
145
-
146
154
  def specified_version_that_does_not_exist?
147
155
  specified? && version_does_not_exist?
148
156
  end
@@ -174,16 +182,25 @@ module PactBroker
174
182
  !ignore?
175
183
  end
176
184
 
177
- # rubocop: disable Metrics/CyclomaticComplexity
185
+ # rubocop: disable Metrics/CyclomaticComplexity, Metrics/MethodLength
178
186
  def description
179
187
  if latest_tagged? && pacticipant_version_number
180
188
  "the latest version of #{pacticipant_name} with tag #{tag} (#{pacticipant_version_number})"
181
189
  elsif latest_tagged?
182
190
  "the latest version of #{pacticipant_name} with tag #{tag} (no such version exists)"
191
+ elsif main_branch? && pacticipant_version_number.nil?
192
+ "a version of #{pacticipant_name} from the main branch (no such version exists)"
193
+ elsif latest_from_main_branch? && pacticipant_version_number.nil?
194
+ "the latest version of #{pacticipant_name} from the main branch (no such verison exists)"
183
195
  elsif latest_from_branch? && pacticipant_version_number
184
196
  "the latest version of #{pacticipant_name} from branch #{branch} (#{pacticipant_version_number})"
185
197
  elsif latest_from_branch?
186
198
  "the latest version of #{pacticipant_name} from branch #{branch} (no such version exists)"
199
+ elsif branch && pacticipant_version_number
200
+ prefix = one_of_many? ? "one of the versions " : "the version "
201
+ prefix + "of #{pacticipant_name} from branch #{branch} (#{pacticipant_version_number})"
202
+ elsif branch
203
+ "a version of #{pacticipant_name} from branch #{branch} (no such version exists)"
187
204
  elsif latest? && pacticipant_version_number
188
205
  "the latest version of #{pacticipant_name} (#{pacticipant_version_number})"
189
206
  elsif latest?
@@ -194,9 +211,9 @@ module PactBroker
194
211
  "a version of #{pacticipant_name} with tag #{tag} (no such version exists)"
195
212
  elsif environment_name && pacticipant_version_number
196
213
  prefix = one_of_many? ? "one of the versions" : "the version"
197
- "#{prefix} of #{pacticipant_name} currently deployed to #{environment_name} (#{pacticipant_version_number})"
214
+ "#{prefix} of #{pacticipant_name} currently deployed or released to #{environment_name} (#{pacticipant_version_number})"
198
215
  elsif environment_name
199
- "the version of #{pacticipant_name} currently deployed to #{environment_name} (no such version exists)"
216
+ "a version of #{pacticipant_name} currently deployed or released to #{environment_name} (no version is currently recorded as deployed/released in this environment)"
200
217
  elsif pacticipant_version_number && version_does_not_exist?
201
218
  "version #{pacticipant_version_number} of #{pacticipant_name} (no such version exists)"
202
219
  elsif pacticipant_version_number
@@ -207,7 +224,27 @@ module PactBroker
207
224
  "any version of #{pacticipant_name}"
208
225
  end
209
226
  end
210
- # rubocop: enable Metrics/CyclomaticComplexity
227
+ # rubocop: enable Metrics/CyclomaticComplexity, Metrics/MethodLength
228
+
229
+ def version_does_not_exist_description
230
+ if version_does_not_exist?
231
+ if tag
232
+ "No version with tag #{tag} exists for #{pacticipant_name}"
233
+ elsif branch
234
+ "No version of #{pacticipant_name} from branch #{branch} exists"
235
+ elsif main_branch?
236
+ "No version of #{pacticipant_name} from the main branch exists"
237
+ elsif environment_name
238
+ "No version of #{pacticipant_name} is currently recorded as deployed or released in environment #{environment_name}"
239
+ elsif pacticipant_version_number
240
+ "No pacts or verifications have been published for version #{pacticipant_version_number} of #{pacticipant_name}"
241
+ else
242
+ "No pacts or verifications have been published for #{pacticipant_name}"
243
+ end
244
+ else
245
+ ""
246
+ end
247
+ end
211
248
  end
212
249
  end
213
250
  end
@@ -87,8 +87,10 @@ module PactBroker
87
87
  error_messages << "Please provide 1 or more version selectors."
88
88
  end
89
89
 
90
- if options[:tag]&.not_blank? && options[:environment_name]&.not_blank?
91
- error_messages << message("errors.validation.cannot_specify_tag_and_environment")
90
+ destination_identifiers = [options[:tag], options[:environment_name], options[:main_branch]&.to_s].compact
91
+
92
+ if destination_identifiers.size > 1
93
+ error_messages << message("errors.validation.cannot_specify_more_than_one_destination_identifier")
92
94
  end
93
95
 
94
96
  if options[:latest] && options[:environment_name]&.not_blank?
@@ -11,7 +11,7 @@ module PactBroker
11
11
 
12
12
  # TODO rename branch to branch_name
13
13
  def self.from_hash(hash)
14
- new(hash.symbolize_keys.snakecase_keys.slice(:pacticipant_name, :pacticipant_version_number, :latest, :tag, :branch, :environment_name))
14
+ new(hash.symbolize_keys.snakecase_keys.slice(:pacticipant_name, :pacticipant_version_number, :latest, :tag, :branch, :environment_name, :main_branch))
15
15
  end
16
16
 
17
17
  def pacticipant_name
@@ -42,6 +42,11 @@ module PactBroker
42
42
  self[:branch]
43
43
  end
44
44
 
45
+ # @return [Boolean]
46
+ def main_branch
47
+ self[:main_branch]
48
+ end
49
+
45
50
  def environment_name
46
51
  self[:environment_name]
47
52
  end
@@ -58,6 +63,11 @@ module PactBroker
58
63
  self[:branch] = branch
59
64
  end
60
65
 
66
+ # @param [Boolean] main_branch
67
+ def main_branch= main_branch
68
+ self[:main_branch] = main_branch
69
+ end
70
+
61
71
  def environment_name= environment_name
62
72
  self[:environment_name] = environment_name
63
73
  end
@@ -79,9 +89,11 @@ module PactBroker
79
89
  self[:max_age]
80
90
  end
81
91
 
92
+ # rubocop: disable Metrics/CyclomaticComplexity
82
93
  def all_for_pacticipant?
83
- !!pacticipant_name && !pacticipant_version_number && !tag && !branch && !latest && !environment_name && !max_age
94
+ !!pacticipant_name && !pacticipant_version_number && !tag && !branch && !latest && !environment_name && !max_age && !main_branch
84
95
  end
96
+ # rubocop: enable Metrics/CyclomaticComplexity
85
97
 
86
98
  def latest_for_pacticipant_and_tag?
87
99
  !!(pacticipant_name && tag && latest)
@@ -23,15 +23,6 @@ module PactBroker
23
23
  message("errors.validation." + key, options)
24
24
  end
25
25
 
26
- def potential_duplicate_pacticipant_message new_name, potential_duplicate_pacticipants, base_url
27
- existing_names = potential_duplicate_pacticipants.
28
- collect{ | p | "* #{p.name}" }.join("\n")
29
- message("errors.duplicate_pacticipant",
30
- new_name: new_name,
31
- existing_names: existing_names,
32
- create_pacticipant_url: pacticipants_url(base_url))
33
- end
34
-
35
26
  def pluralize(word, count)
36
27
  if count == 1
37
28
  word
@@ -43,11 +34,5 @@ module PactBroker
43
34
  end
44
35
  end
45
36
  end
46
-
47
- private
48
-
49
- def pacticipants_url base_url
50
- PactBroker::Api::PactBrokerUrls.pacticipants_url base_url
51
- end
52
37
  end
53
38
  end
@@ -10,13 +10,14 @@ module PactBroker
10
10
  extend PactBroker::Repositories
11
11
  extend PactBroker::Services
12
12
  include PactBroker::Logging
13
+ extend PactBroker::Messages
13
14
 
14
15
  def self.messages_for_potential_duplicate_pacticipants(pacticipant_names, base_url)
15
16
  messages = []
16
17
  pacticipant_names.each do | name |
17
18
  potential_duplicate_pacticipants = find_potential_duplicate_pacticipants(name)
18
19
  if potential_duplicate_pacticipants.any?
19
- messages << Messages.potential_duplicate_pacticipant_message(name, potential_duplicate_pacticipants, base_url)
20
+ messages << potential_duplicate_pacticipant_message(name, potential_duplicate_pacticipants, base_url)
20
21
  end
21
22
  end
22
23
  messages
@@ -98,6 +99,15 @@ module PactBroker
98
99
  pacticipant
99
100
  end
100
101
  end
102
+
103
+ private_class_method def self.potential_duplicate_pacticipant_message(new_name, potential_duplicate_pacticipants, base_url)
104
+ existing_names = potential_duplicate_pacticipants.
105
+ collect{ | p | "* #{p.name}" }.join("\n")
106
+ message("errors.duplicate_pacticipant",
107
+ new_name: new_name,
108
+ existing_names: existing_names,
109
+ base_url: base_url)
110
+ end
101
111
  end
102
112
  end
103
113
  end
@@ -31,7 +31,7 @@ module PactBroker
31
31
  version_text = head_consumer_tags.size == 1 || branches.size == 1 ? "version" : "versions"
32
32
  if wip?
33
33
  # WIP pacts will always have tags, because it is part of the definition of being a WIP pact
34
- "The pact at #{pact_version_url} is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest #{version_text} of #{consumer_name} #{joined_head_consumer_tags_and_branches} and is still in pending state). #{READ_MORE_WIP}"
34
+ "The pact at #{pact_version_url} is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest #{version_text} of #{consumer_name} #{joined_head_consumer_tags_and_branches} and it has not yet been successfully verified by #{pending_provider_branch_or_tags_description("a")} when the pact's application version was explicitly specified in the consumer version selectors). #{READ_MORE_WIP}".tap { |it| puts it }
35
35
  else
36
36
  criteria_or_criterion = selectors.size > 1 ? "criteria" : "criterion"
37
37
  version_or_versions = pluralize("the consumer version", selectors.size)
@@ -40,7 +40,9 @@ module PactBroker
40
40
  end
41
41
 
42
42
  def pending_reason
43
- if pending?
43
+ if pending? && wip?
44
+ "This pact is in pending state for this version of #{provider_name} because it was included as a 'work in progress' pact. If this verification fails, it will not cause the overall build to fail. #{READ_MORE_PENDING}"
45
+ elsif pending?
44
46
  "This pact is in pending state for this version of #{provider_name} because a successful verification result for #{pending_provider_branch_or_tags_description("a")} has not yet been published. If this verification fails, it will not cause the overall build to fail. #{READ_MORE_PENDING}"
45
47
  else
46
48
  "This pact has previously been successfully verified by #{non_pending_provider_branch_or_tags_description}. If this verification fails, it will fail the build. #{READ_MORE_PENDING}"
@@ -55,6 +55,19 @@ module PactBroker
55
55
  select_append(Sequel[model.table_name].*)
56
56
  end
57
57
 
58
+ # @param [Symbol] max_column the name of the column of which to calculate the maxiumum
59
+ # @param [Array<Symbol>] group_by_columns the names of the columns by which to group
60
+ def max_group_by(max_column, group_by_columns, &extra_criteria_block)
61
+ maximums_base_query = extra_criteria_block ? extra_criteria_block.call(self) : self
62
+ maximums = maximums_base_query.select_group(*group_by_columns).select_append(Sequel.function(:max, max_column).as(:max_value))
63
+
64
+ max_join = group_by_columns.each_with_object({ Sequel[:maximums][:max_value] => max_column }) do | column_name, joins |
65
+ joins[Sequel[:maximums][column_name]] = column_name
66
+ end
67
+
68
+ join(maximums, max_join, table_alias: :maximums)
69
+ end
70
+
58
71
  def select_for_subquery column
59
72
  if mysql? #stoopid mysql doesn't allow you to modify datasets with subqueries
60
73
  column_name = column.respond_to?(:alias) ? column.alias : column
@@ -323,6 +323,20 @@ module PactBroker
323
323
  self
324
324
  end
325
325
 
326
+ def can_i_merge(pacticipant:, version:)
327
+ can_i_merge_response = client.get("matrix", { q: [pacticipant: pacticipant, version: version], latestby: "cvp", mainBranch: true, latest: true }.compact ).tap { |response| check_for_error(response) }
328
+ can = !!(can_i_merge_response.body["summary"] || {})["deployable"]
329
+ puts "can-i-merge #{pacticipant} version #{version}: #{can ? 'yes' : 'no'}"
330
+ summary = can_i_merge_response.body["summary"]
331
+ verification_result_urls = (can_i_merge_response.body["matrix"] || []).collect do | row |
332
+ row.dig("verificationResult", "_links", "self", "href")
333
+ end.compact
334
+ summary.merge!("verification_result_urls" => verification_result_urls)
335
+ puts summary.to_yaml
336
+ separate
337
+ self
338
+ end
339
+
326
340
  def delete_integration(consumer:, provider:)
327
341
  puts "Deleting all data for the integration between #{consumer} and #{provider}"
328
342
  client.delete("integrations/provider/#{encode(provider)}/consumer/#{encode(consumer)}").tap { |response| check_for_error(response) }
@@ -70,12 +70,14 @@
70
70
  %input{name: 'q[]latest', value: 'true', hidden: true, class: 'latest-flag'}
71
71
 
72
72
 
73
- - if options.latest || options.tag
73
+ %input{name: 'latest', value: options.latest.to_s, hidden: true }
74
+ %input{name: 'mainBranch', value: options.mainBranch.to_s, hidden: true}
75
+
76
+ - if options.tag
74
77
  .selector
75
78
  %label{for: 'to'}
76
79
  = options.latest ? 'To (tag)' : 'With all (tagged)'
77
80
  %input{name: 'tag', id: 'to', value: options.tag }
78
- %input{name: 'latest', value: options.latest.to_s, hidden: true}
79
81
 
80
82
  - if options.environment_name
81
83
  .selector
@@ -83,6 +85,7 @@
83
85
  To environment
84
86
  %input{name: 'environment', id: 'environment', value: options.environment_name }
85
87
 
88
+
86
89
  %div.top-of-group.form-check
87
90
  %input{type: 'radio', name: "latestby", class: 'form-check-input', value: '', id: 'all_rows', checked: options.all_rows_checked}
88
91
  %label{for: 'all_rows', class: "form-check-label"}
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = "2.99.0"
2
+ VERSION = "2.100.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.99.0
4
+ version: 2.100.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-05-16 00:00:00.000000000 Z
13
+ date: 2022-05-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -1222,7 +1222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1222
1222
  - !ruby/object:Gem::Version
1223
1223
  version: '0'
1224
1224
  requirements: []
1225
- rubygems_version: 3.3.13
1225
+ rubygems_version: 3.3.14
1226
1226
  signing_key:
1227
1227
  specification_version: 4
1228
1228
  summary: See description