pact_broker 2.71.0 → 2.72.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +1 -21
  3. data/CHANGELOG.md +11 -0
  4. data/Dockerfile +5 -1
  5. data/docker-compose-ci-mysql.yml +37 -0
  6. data/lib/pact_broker/api/pact_broker_urls.rb +5 -1
  7. data/lib/pact_broker/api/resources/metadata_resource_methods.rb +23 -0
  8. data/lib/pact_broker/api/resources/pact.rb +2 -13
  9. data/lib/pact_broker/api/resources/pact_resource_methods.rb +23 -0
  10. data/lib/pact_broker/api/resources/pact_version.rb +3 -0
  11. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +4 -0
  12. data/lib/pact_broker/api/resources/verifications.rb +2 -4
  13. data/lib/pact_broker/db/clean_incremental.rb +132 -22
  14. data/lib/pact_broker/db/delete_overwritten_data.rb +55 -27
  15. data/lib/pact_broker/domain/tag.rb +42 -0
  16. data/lib/pact_broker/domain/verification.rb +87 -0
  17. data/lib/pact_broker/metrics/service.rb +5 -3
  18. data/lib/pact_broker/pacts/all_pact_publications.rb +8 -0
  19. data/lib/pact_broker/pacts/repository.rb +35 -11
  20. data/lib/pact_broker/tasks/clean_task.rb +9 -3
  21. data/lib/pact_broker/tasks/delete_overwritten_data_task.rb +23 -7
  22. data/lib/pact_broker/test/test_data_builder.rb +24 -0
  23. data/lib/pact_broker/version.rb +1 -1
  24. data/script/docker-container/test.sh +3 -0
  25. data/script/docker/db-psql.sh +3 -0
  26. data/script/docker/db-reload.sh +11 -0
  27. data/script/pry.rb +25 -0
  28. data/script/seed.rb +1 -0
  29. data/script/test/run-rake-on-docker-compose-mysql.sh +8 -0
  30. data/spec/features/delete_tagged_pact_versions_spec.rb +2 -2
  31. data/spec/features/get_pact_spec.rb +2 -2
  32. data/spec/features/get_pact_version.rb +26 -3
  33. data/spec/fixtures/approvals/clean_incremental_dry_run.approved.json +100 -0
  34. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +6 -0
  35. data/spec/lib/pact_broker/api/resources/pact_spec.rb +20 -9
  36. data/spec/lib/pact_broker/api/resources/tagged_pact_versions_spec.rb +10 -2
  37. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +7 -3
  38. data/spec/lib/pact_broker/db/clean_incremental_spec.rb +9 -1
  39. data/spec/lib/pact_broker/db/delete_overwritten_data_spec.rb +71 -11
  40. data/spec/lib/pact_broker/domain/tag_spec.rb +23 -9
  41. data/spec/lib/pact_broker/domain/verification_spec.rb +49 -0
  42. data/spec/lib/pact_broker/metrics/service_spec.rb +4 -1
  43. data/spec/lib/pact_broker/pacts/repository_spec.rb +54 -7
  44. data/spec/migrations/change_migration_strategy_spec.rb +1 -1
  45. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7fe157577be6cdfb603e4607b174224781db0b9b2ee7ccb1325cdda50ce129d
4
- data.tar.gz: 9e28ef38f281ac6d6dbf1879d3794a8f915cb78bb8a82c464c7273ac9f8522dc
3
+ metadata.gz: f848cc7a613de4d084d69d97bf1b835c1d1bb130eec3c4c968800e348daf95bf
4
+ data.tar.gz: 1cbbbd2832eb98cd3904bba8aff8d909a95663c70abe7ad710473e46b3fb3e7c
5
5
  SHA512:
6
- metadata.gz: 3e5d4d249f691a3e03725e3b827f4044eb2fde360cd0dd9128fce61a4442288083f825f654473cf1a3220745460675036951242cc801211eda258369e38eeb09
7
- data.tar.gz: f8b9f3bdbfe42ca21291b41588ae4b3be15ca53efaec275e5356d8363dfa181e592942cb5e13bbd492fadb61d231cee68aaed009fef559c0645bc4debc4137c1
6
+ metadata.gz: 2200189e21700c21b67e1f8abc85574c3ed9367287568ce7dab4f808cd81ae01a80acd60fe3309cb134cd4466d2c243244429073d4e3af53b6b009bc58848782
7
+ data.tar.gz: c304534b3125112f4bd2bead0509e7e711face5f0e84ec88594cae1eb6e14a1618a4e61884ffbb0f66dbd813c4e5d4ebd61867ce276120a471a6a77f4c1e6e61
@@ -51,24 +51,4 @@ jobs:
51
51
  ruby_version: ["2.7"]
52
52
  steps:
53
53
  - uses: actions/checkout@v2
54
- - uses: actions/setup-ruby@v1
55
- with:
56
- ruby-version: ${{ matrix.ruby_version }}
57
- - uses: mirromutth/mysql-action@v1.1
58
- with:
59
- host port: 3306 # Optional, default value is 3306. The port of host
60
- #container port: 3307 # Optional, default value is 3306. The port of container
61
- character set server: 'utf8' # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld
62
- collation server: 'utf8_general_ci' # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld
63
- mysql version: '8.0' # Optional, default value is "latest". The version of the MySQL
64
- mysql database: 'pact_broker' # Optional, default value is "test". The specified database which will be create
65
- mysql root password: 'pact_broker' # Required if "mysql user" is empty, default is empty. The root superuser password
66
- mysql user: 'pact_broker' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too
67
- mysql password: 'pact_broker' # Required if "mysql user" exists. The password for the "mysql user"
68
- - run: "bundle install"
69
- env:
70
- INSTALL_MYSQL: "true"
71
- - run: "bundle exec rake"
72
- env:
73
- DATABASE_ADAPTER: github_actions_mysql
74
- INSTALL_MYSQL: "true"
54
+ - run: script/test/run-rake-on-docker-compose-mysql.sh
@@ -1,3 +1,14 @@
1
+ <a name="v2.72.0"></a>
2
+ ### v2.72.0 (2020-12-02)
3
+
4
+ #### Features
5
+
6
+ * allow overwritten data deletion to be configured with extra options ([fd809737](/../../commit/fd809737))
7
+ * use the consumer version number in the metadata to select the correct consumer version for a pact version resource ([422c87fc](/../../commit/422c87fc))
8
+ * return link to latest pact if more pacts exist when deleting pacts by tag ([b87ea704](/../../commit/b87ea704))
9
+ * update output for clean dry run ([681a5ddd](/../../commit/681a5ddd))
10
+ * update metrics output ([0617e9df](/../../commit/0617e9df))
11
+
1
12
  <a name="v2.71.0"></a>
2
13
  ### v2.71.0 (2020-11-28)
3
14
 
data/Dockerfile CHANGED
@@ -26,7 +26,7 @@ RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSI
26
26
 
27
27
 
28
28
  COPY Gemfile /home/Gemfile
29
- COPY Gemfile.lock /home/Gemfile.lock
29
+ # COPY Gemfile.lock /home/Gemfile.lock # lock file does not exist on CI
30
30
  COPY pact_broker.gemspec /home/pact_broker.gemspec
31
31
  COPY lib/pact_broker/version.rb /home/lib/pact_broker/version.rb
32
32
  COPY .gitignore /home/.gitignore
@@ -38,6 +38,10 @@ RUN echo '#!/bin/sh' >> /usr/local/bin/start
38
38
  RUN echo 'bundle exec rackup -o 0.0.0.0 -p 9292' >> /usr/local/bin/start
39
39
  RUN chmod +x /usr/local/bin/start
40
40
 
41
+ RUN echo '#!/bin/sh' >> /usr/local/bin/test
42
+ RUN echo 'bundle exec rake' >> /usr/local/bin/test
43
+ RUN chmod +x /usr/local/bin/test
44
+
41
45
  RUN echo '#!/bin/sh' >> /home/init-db.sh
42
46
  RUN echo 'bundle exec rake db:prepare:test' >> /home/init-db.sh
43
47
  RUN chmod +x /home/init-db.sh
@@ -0,0 +1,37 @@
1
+ version: "3"
2
+
3
+ services:
4
+ mysql:
5
+ image: mysql:5.7.28
6
+ command: --default-authentication-plugin=mysql_native_password
7
+ environment:
8
+ MYSQL_ROOT_PASSWORD: pact_broker
9
+ MYSQL_DATABASE: pact_broker
10
+ MYSQL_USER: pact_broker
11
+ MYSQL_PASSWORD: pact_broker
12
+ ports:
13
+ - "3306:3306"
14
+
15
+ tests:
16
+ build: .
17
+ depends_on:
18
+ - mysql
19
+ environment:
20
+ DATABASE_ADAPTER: docker_compose_mysql
21
+ PACT_BROKER_HIDE_PACTFLOW_MESSAGES: 'true'
22
+ INSTALL_MYSQL: 'true'
23
+ volumes:
24
+ - ./lib:/home/lib
25
+ - ./spec:/home/spec
26
+ - ./db:/home/db
27
+ - ./config.ru:/home/config.ru
28
+ - ./tasks:/home/tasks
29
+ - ./Rakefile:/home/Rakefile
30
+ - ./config:/home/config
31
+ - ./.rspec:/home/.rspec
32
+ - ./.rubocop:/home/.rubocop
33
+ - ./.gitignore:/home/.gitignore
34
+ - ./public:/home/public
35
+ - ./script/docker-container/test.sh:/usr/local/bin/test
36
+ entrypoint: dockerize
37
+ command: --wait tcp://mysql:3306 -timeout 60s test
@@ -75,7 +75,7 @@ module PactBroker
75
75
  end
76
76
 
77
77
  def decode_pact_metadata(metadata)
78
- if metadata
78
+ if metadata && metadata != ''
79
79
  begin
80
80
  Rack::Utils.parse_nested_query(Base64.strict_decode64(metadata)).each_with_object({}) do | (k, v), new_hash |
81
81
  new_hash[k.to_sym] = v
@@ -116,6 +116,10 @@ module PactBroker
116
116
  "#{base_url}/pacts/provider/#{url_encode(provider_name)}/consumer/#{url_encode(consumer_name)}/versions"
117
117
  end
118
118
 
119
+ def tagged_pact_versions_url consumer_name, provider_name, tag, base_url = ""
120
+ "#{base_url}/pacts/provider/#{url_encode(provider_name)}/consumer/#{url_encode(consumer_name)}/tag/#{url_encode(tag)}"
121
+ end
122
+
119
123
  def integration_url consumer_name, provider_name, base_url = ""
120
124
  "#{base_url}/integrations/provider/#{url_encode(provider_name)}/consumer/#{url_encode(consumer_name)}"
121
125
  end
@@ -0,0 +1,23 @@
1
+ require 'pact_broker/hash_refinements'
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ module MetadataResourceMethods
7
+ using PactBroker::HashRefinements
8
+
9
+ def pact_params
10
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request(request, maybe_params_with_consumer_version_number.merge(path_info))
11
+ end
12
+
13
+ def maybe_params_with_consumer_version_number
14
+ metadata.slice(:consumer_version_number)
15
+ end
16
+
17
+ def metadata
18
+ @metadata ||= PactBrokerUrls.decode_pact_metadata(identifier_from_path[:metadata])
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -8,6 +8,7 @@ require 'pact_broker/pacts/pact_params'
8
8
  require 'pact_broker/api/contracts/put_pact_params_contract'
9
9
  require 'pact_broker/webhooks/execution_configuration'
10
10
  require 'pact_broker/api/resources/webhook_execution_methods'
11
+ require 'pact_broker/api/resources/pact_resource_methods'
11
12
 
12
13
  module PactBroker
13
14
  module Api
@@ -15,6 +16,7 @@ module PactBroker
15
16
  class Pact < BaseResource
16
17
  include PacticipantResourceMethods
17
18
  include WebhookExecutionMethods
19
+ include PactResourceMethods
18
20
 
19
21
  def content_types_provided
20
22
  [
@@ -110,19 +112,6 @@ module PactBroker
110
112
  @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
111
113
  end
112
114
 
113
- def set_post_deletion_response
114
- latest_pact = pact_service.find_latest_pact(pact_params)
115
- response_body = { "_links" => { index: { href: base_url } } }
116
- if latest_pact
117
- response_body["_links"]["pb:latest-pact-version"] = {
118
- href: latest_pact_url(base_url, latest_pact),
119
- title: "Latest pact"
120
- }
121
- end
122
- response.body = response_body.to_json
123
- response.headers["Content-Type" => "application/hal+json;charset=utf-8"]
124
- end
125
-
126
115
  def webhook_options
127
116
  {
128
117
  database_connector: database_connector,
@@ -0,0 +1,23 @@
1
+ module PactBroker
2
+ module Api
3
+ module Resources
4
+ module PactResourceMethods
5
+ def set_post_deletion_response
6
+ latest_pact = pact_service.find_latest_pact(
7
+ consumer_name: pact_params[:consumer_name],
8
+ provider_name: pact_params[:provider_name]
9
+ )
10
+ response_body = { "_links" => { index: { href: base_url } } }
11
+ if latest_pact
12
+ response_body["_links"]["pb:latest-pact-version"] = {
13
+ href: latest_pact_url(base_url, latest_pact),
14
+ title: "Latest pact"
15
+ }
16
+ end
17
+ response.body = response_body.to_json
18
+ response.headers["Content-Type" => "application/hal+json;charset=utf-8"]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,9 +1,12 @@
1
1
  require 'pact_broker/api/resources/pact'
2
+ require 'pact_broker/api/resources/metadata_resource_methods'
2
3
 
3
4
  module PactBroker
4
5
  module Api
5
6
  module Resources
6
7
  class PactVersion < Pact
8
+ include MetadataResourceMethods
9
+
7
10
  def allowed_methods
8
11
  ["GET", "OPTIONS"]
9
12
  end
@@ -1,11 +1,14 @@
1
1
  require 'pact_broker/api/resources/base_resource'
2
2
  require 'pact_broker/configuration'
3
3
  require 'pact_broker/api/decorators/tagged_pact_versions_decorator'
4
+ require 'pact_broker/api/resources/pact_resource_methods'
4
5
 
5
6
  module PactBroker
6
7
  module Api
7
8
  module Resources
8
9
  class TaggedPactVersions < BaseResource
10
+ include PactResourceMethods
11
+
9
12
  def content_types_provided
10
13
  [["application/hal+json", :to_json]]
11
14
  end
@@ -24,6 +27,7 @@ module PactBroker
24
27
 
25
28
  def delete_resource
26
29
  pact_service.delete_all_pact_publications_between consumer_name, and: provider_name, tag: identifier_from_path[:tag]
30
+ set_post_deletion_response
27
31
  true
28
32
  end
29
33
 
@@ -4,12 +4,14 @@ require 'pact_broker/domain/verification'
4
4
  require 'pact_broker/api/contracts/verification_contract'
5
5
  require 'pact_broker/api/decorators/verification_decorator'
6
6
  require 'pact_broker/api/resources/webhook_execution_methods'
7
+ require 'pact_broker/api/resources/metadata_resource_methods'
7
8
 
8
9
  module PactBroker
9
10
  module Api
10
11
  module Resources
11
12
  class Verifications < BaseResource
12
13
  include WebhookExecutionMethods
14
+ include MetadataResourceMethods
13
15
 
14
16
  def content_types_accepted
15
17
  [["application/json", :from_json]]
@@ -75,10 +77,6 @@ module PactBroker
75
77
  PactBroker::Api::Decorators::VerificationDecorator.new(model)
76
78
  end
77
79
 
78
- def metadata
79
- PactBrokerUrls.decode_pact_metadata(identifier_from_path[:metadata])
80
- end
81
-
82
80
  def wip?
83
81
  metadata[:wip] == 'true'
84
82
  end
@@ -43,35 +43,20 @@ module PactBroker
43
43
  end
44
44
 
45
45
  def version_ids_to_keep
46
- @version_ids_to_keep ||= keep.collect do | selector |
46
+ @version_ids_to_keep ||= selected_versions_to_keep.reduce(&:union)
47
+ end
48
+
49
+ def selected_versions_to_keep
50
+ keep.collect do | selector |
47
51
  PactBroker::Domain::Version.select(:id).for_selector(selector)
48
- end.reduce(&:union)
52
+ end
49
53
  end
50
54
 
51
55
  def call
52
56
  require 'pact_broker/db/models'
53
57
 
54
58
  if dry_run?
55
- to_delete = PactBroker::Domain::Version
56
- .where(id: version_ids_to_delete.select(:id))
57
- .all
58
- .group_by{ | v | v.pacticipant_id }
59
- .each_with_object({}) do | (pacticipant_id, versions), thing |
60
- thing[versions.first.pacticipant.name] = {
61
- count: versions.count,
62
- from_version: {
63
- number: versions.first.number,
64
- created: DateHelper.distance_of_time_in_words(versions.first.created_at, DateTime.now) + " ago",
65
- tags: versions.first.tags.collect(&:name)
66
- },
67
- to_version: {
68
- number: versions.last.number,
69
- created: DateHelper.distance_of_time_in_words(versions.last.created_at, DateTime.now) + " ago",
70
- tags: versions.last.tags.collect(&:name)
71
- }
72
- }
73
- end
74
- { "to_delete" => to_delete }
59
+ dry_run_results
75
60
  else
76
61
  before_counts = current_counts
77
62
  result = PactBroker::Domain::Version.where(id: resolve_ids(version_ids_to_delete)).delete
@@ -102,6 +87,131 @@ module PactBroker
102
87
  referenced_pact_version_ids = db[:pact_publications].select(:pact_version_id).union(db[:verifications].select(:pact_version_id))
103
88
  db[:pact_versions].where(id: referenced_pact_version_ids).invert.delete
104
89
  end
90
+
91
+ def version_info(version)
92
+ {
93
+ "number" => version.number,
94
+ "created" => DateHelper.distance_of_time_in_words(version.created_at, DateTime.now) + " ago",
95
+ "tags" => version.tags.collect(&:name).sort
96
+ }
97
+ end
98
+
99
+ def dry_run_results
100
+ to_delete = dry_run_to_delete
101
+ to_keep = dry_run_to_keep
102
+
103
+ kept_per_selector = keep.collect do | selector |
104
+ {
105
+ selector: selector.to_hash,
106
+ count: PactBroker::Domain::Version.for_selector(selector).count
107
+ }
108
+ end
109
+
110
+ pacticipant_results = pacticipants.each_with_object({}) do | pacticipant, results |
111
+ results[pacticipant.name] = {
112
+ "toDelete" => to_delete[pacticipant.name] || { "count" => 0 },
113
+ "toKeep" => to_keep[pacticipant.id]
114
+ }
115
+ end
116
+
117
+ total_versions_count = PactBroker::Domain::Version.count
118
+ versions_to_keep_count = version_ids_to_keep.count
119
+ versions_to_delete_count = version_ids_to_delete.count
120
+
121
+ {
122
+ "counts" => {
123
+ "totalVersions" => total_versions_count,
124
+ "versionsToDelete" => versions_to_delete_count,
125
+ "versionsNotToKeep" => total_versions_count - versions_to_keep_count,
126
+ "versionsToKeep" => versions_to_keep_count,
127
+ "versionsToKeepBySelector" => kept_per_selector,
128
+ },
129
+ "versionSummary" => pacticipant_results
130
+ }
131
+ end
132
+
133
+ def dry_run_latest_versions_to_keep
134
+ latest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id))
135
+ .invert
136
+ .select_group(:pacticipant_id)
137
+ .select_append{ max(order).as(latest_order) }
138
+
139
+ lv_versions_join = {
140
+ Sequel[:lv][:latest_order] => Sequel[:versions][:order],
141
+ Sequel[:lv][:pacticipant_id] => Sequel[:versions][:pacticipant_id]
142
+ }
143
+
144
+ PactBroker::Domain::Version
145
+ .select_all_qualified
146
+ .join(latest_undeleted_versions_by_order, lv_versions_join, { table_alias: :lv })
147
+ end
148
+
149
+ def dry_run_earliest_versions_to_keep
150
+ earliest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id))
151
+ .invert
152
+ .select_group(:pacticipant_id)
153
+ .select_append{ min(order).as(first_order) }
154
+
155
+ ev_versions_join = {
156
+ Sequel[:lv][:first_order] => Sequel[:versions][:order],
157
+ Sequel[:lv][:pacticipant_id] => Sequel[:versions][:pacticipant_id]
158
+ }
159
+
160
+ PactBroker::Domain::Version
161
+ .select_all_qualified
162
+ .join(earliest_undeleted_versions_by_order, ev_versions_join, { table_alias: :lv })
163
+ end
164
+
165
+ def dry_run_to_delete
166
+ PactBroker::Domain::Version
167
+ .where(id: version_ids_to_delete.select(:id))
168
+ .all
169
+ .group_by{ | v | v.pacticipant_id }
170
+ .each_with_object({}) do | (pacticipant_id, versions), thing |
171
+ thing[versions.first.pacticipant.name] = {
172
+ "count" => versions.count,
173
+ "fromVersion" => version_info(versions.first),
174
+ "toVersion" => version_info(versions.last)
175
+ }
176
+ end
177
+ end
178
+
179
+ def dry_run_to_keep
180
+ latest_to_keep = dry_run_latest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r |
181
+ r[version.pacticipant_id] = {
182
+ "firstVersion" => version_info(version)
183
+ }
184
+ end
185
+
186
+ earliest_to_keep = dry_run_earliest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r |
187
+ r[version.pacticipant_id] = {
188
+ "latestVersion" => version_info(version)
189
+ }
190
+ end
191
+
192
+ counts = counts_to_keep
193
+
194
+ pacticipants.collect(&:id).each_with_object({}) do | pacticipant_id, results |
195
+ results[pacticipant_id] = { "count" => counts[pacticipant_id] || 0 }
196
+ .merge(earliest_to_keep[pacticipant_id] || {})
197
+ .merge(latest_to_keep[pacticipant_id] || {})
198
+ end
199
+ end
200
+
201
+ def counts_to_keep
202
+ db[:versions].where(id: version_ids_to_delete.select(:id))
203
+ .invert
204
+ .select_group(:pacticipant_id)
205
+ .select_append{ count(1).as(count) }
206
+ .all
207
+ .each_with_object({}) do | row, counts |
208
+ counts[row[:pacticipant_id]] = row[:count]
209
+ end
210
+ end
211
+
212
+ def pacticipants
213
+ @pacticipants ||= PactBroker::Domain::Pacticipant.order_ignore_case(:name).all
214
+ end
105
215
  end
106
216
  end
107
217
  end