pact_broker 2.69.0 → 2.70.0

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +62 -0
  3. data/CHANGELOG.md +16 -0
  4. data/DEVELOPER_SETUP.md +6 -0
  5. data/Dockerfile +4 -3
  6. data/Gemfile +4 -0
  7. data/ISSUES.md +23 -0
  8. data/config/database.yml +14 -0
  9. data/docker-compose-issue-repro.yml +11 -7
  10. data/lib/pact_broker/api/resources/clean.rb +36 -0
  11. data/lib/pact_broker/api/resources/group.rb +5 -1
  12. data/lib/pact_broker/db/clean.rb +4 -1
  13. data/lib/pact_broker/db/clean_incremental.rb +78 -0
  14. data/lib/pact_broker/domain/tag.rb +69 -8
  15. data/lib/pact_broker/domain/verification.rb +6 -0
  16. data/lib/pact_broker/domain/version.rb +35 -1
  17. data/lib/pact_broker/logging.rb +7 -0
  18. data/lib/pact_broker/matrix/unresolved_selector.rb +8 -0
  19. data/lib/pact_broker/pacts/all_pact_publications.rb +1 -3
  20. data/lib/pact_broker/pacts/pact_publication.rb +7 -1
  21. data/lib/pact_broker/pacts/repository.rb +4 -2
  22. data/lib/pact_broker/tasks/clean_task.rb +28 -12
  23. data/lib/pact_broker/test/http_test_data_builder.rb +52 -5
  24. data/lib/pact_broker/version.rb +1 -1
  25. data/lib/pact_broker/webhooks/triggered_webhook.rb +6 -0
  26. data/pact_broker.gemspec +1 -1
  27. data/script/docker/db-start.sh +1 -1
  28. data/script/reproduce-issue.rb +38 -15
  29. data/spec/fixtures/approvals/modifiable_resources.approved.json +3 -0
  30. data/spec/lib/pact_broker/api/resources/group_spec.rb +9 -7
  31. data/spec/lib/pact_broker/db/clean_incremental_spec.rb +93 -0
  32. data/spec/lib/pact_broker/domain/tag_spec.rb +46 -0
  33. data/spec/lib/pact_broker/domain/verification_spec.rb +13 -0
  34. data/spec/lib/pact_broker/domain/version_spec.rb +23 -0
  35. data/spec/lib/pact_broker/pacts/pact_publication_spec.rb +3 -1
  36. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +14 -1
  37. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03c656c61487fd81baf9eb94f73fa12ab27bdf5cea1bd2d670af63199123b6ef
4
- data.tar.gz: '04022874b3eeb6681e79163431ce63c8d6760741dc7ec8db30cc87b8f9257a94'
3
+ metadata.gz: b57a2f04694ce119675cd5b4fb647e1c2faff71b890958391ca30846ef0d7fab
4
+ data.tar.gz: 8b6484329b0821600417fc17ad564a14de06edf2edc21939423c34ea2795cee1
5
5
  SHA512:
6
- metadata.gz: ce0f9fb3186ab25c419b61665245977abfbaa8d245437699c44de54783bde86d8281b950172532d02a31f6aa33f36046dde7a0636075fabe75f3f074c33016c2
7
- data.tar.gz: f6b8baa8a66b80b50df9223086f8e67dd9233106897b15f5cfa16328e954805de786d4fbe672f3cee5ba831001edd78f5c0a977dfc5d767d4c79aa46a61b1010
6
+ metadata.gz: e4d1c82adbbb041f38d64200ccb0f1fc8d4672f3a9e445db0dcf428b25c8353c5922a3fb579b74560114df476886fb8aee93d2b793643ae9bbbbd8f786fec443
7
+ data.tar.gz: dc130a9cea4a62c741c401290b385f807c4597073a1ae467d3b334bd058267eca2179ebb6be60d53c2f6cf7046c7b567edf473738a2642e122d1c6e40938301a
@@ -0,0 +1,62 @@
1
+ name: Test
2
+
3
+ on: push
4
+
5
+ jobs:
6
+ postgres:
7
+ runs-on: "ubuntu-latest"
8
+ strategy:
9
+ matrix:
10
+ ruby_version: ["2.5", "2.7"]
11
+ services:
12
+ postgres:
13
+ image: postgres
14
+ env:
15
+ POSTGRES_PASSWORD: postgres
16
+ options: >-
17
+ --health-cmd pg_isready
18
+ --health-interval 10s
19
+ --health-timeout 5s
20
+ --health-retries 5
21
+ ports:
22
+ - 5432:5432
23
+ steps:
24
+ - uses: actions/checkout@v2
25
+ - uses: actions/setup-ruby@v1
26
+ with:
27
+ ruby-version: ${{ matrix.ruby_version }}
28
+ - run: "bundle install"
29
+ env:
30
+ INSTALL_PG: "true"
31
+ - run: "bundle exec rake"
32
+ env:
33
+ DATABASE_ADAPTER: github_actions_postgres
34
+ INSTALL_PG: "true"
35
+ mysql:
36
+ runs-on: "ubuntu-latest"
37
+ strategy:
38
+ matrix:
39
+ ruby_version: ["2.5", "2.7"]
40
+ steps:
41
+ - uses: actions/checkout@v2
42
+ - uses: actions/setup-ruby@v1
43
+ with:
44
+ ruby-version: ${{ matrix.ruby_version }}
45
+ - uses: mirromutth/mysql-action@v1.1
46
+ with:
47
+ host port: 3306 # Optional, default value is 3306. The port of host
48
+ #container port: 3307 # Optional, default value is 3306. The port of container
49
+ character set server: 'utf8' # Optional, default value is 'utf8mb4'. The '--character-set-server' option for mysqld
50
+ collation server: 'utf8_general_ci' # Optional, default value is 'utf8mb4_general_ci'. The '--collation-server' option for mysqld
51
+ mysql version: '8.0' # Optional, default value is "latest". The version of the MySQL
52
+ mysql database: 'pact_broker' # Optional, default value is "test". The specified database which will be create
53
+ mysql root password: 'pact_broker' # Required if "mysql user" is empty, default is empty. The root superuser password
54
+ mysql user: 'pact_broker' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too
55
+ mysql password: 'pact_broker' # Required if "mysql user" exists. The password for the "mysql user"
56
+ - run: "bundle install"
57
+ env:
58
+ INSTALL_MYSQL: "true"
59
+ - run: "bundle exec rake"
60
+ env:
61
+ DATABASE_ADAPTER: github_actions_mysql
62
+ INSTALL_MYSQL: "true"
@@ -1,3 +1,19 @@
1
+ <a name="v2.70.0"></a>
2
+ ### v2.70.0 (2020-11-28)
3
+
4
+ #### Features
5
+
6
+ * allow limit to be applied to clean task ([d29e5c62](/../../commit/d29e5c62))
7
+ * optimise the query to load the tags with latest flags ([bc47613f](/../../commit/bc47613f))
8
+ * optimise query to find head tags for a pact ([67309e37](/../../commit/67309e37))
9
+
10
+ * **wip pacts**
11
+ * experimental feature - if no provider versions exist, consider all head pacts wip ([a635cc53](/../../commit/a635cc53))
12
+
13
+ #### Bug Fixes
14
+
15
+ * return empty body when group csv is requested for a pacticipant that does not have any integrations ([fb4e28ce](/../../commit/fb4e28ce))
16
+
1
17
  <a name="v2.69.0"></a>
2
18
  ### v2.69.0 (2020-11-24)
3
19
 
@@ -28,6 +28,12 @@ This allows you to open a shell to a development environment where you can run t
28
28
 
29
29
  Remember to rebuild the image if you change any of the gems or gem versions.
30
30
 
31
+ * Run the server on the docker image
32
+
33
+ ```sh
34
+ docker run --rm -v $(PWD):/home -w /home --entrypoint /bin/sh -p 9292:9292 -it pact_broker:dev /usr/local/bin/start
35
+ ```
36
+
31
37
  ### With native install
32
38
 
33
39
  * You will need to install Ruby 2.5, and preferably a ruby version manager. I recommend using [chruby][chruby] and [ruby-install][ruby-install].
data/Dockerfile CHANGED
@@ -26,6 +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
30
  COPY pact_broker.gemspec /home/pact_broker.gemspec
30
31
  COPY lib/pact_broker/version.rb /home/lib/pact_broker/version.rb
31
32
  COPY .gitignore /home/.gitignore
@@ -33,9 +34,9 @@ COPY .gitignore /home/.gitignore
33
34
  RUN gem install bundler -v '~>2.0.0' \
34
35
  && bundle install --jobs 3 --retry 3
35
36
 
36
- RUN echo '#!/bin/sh' >> /home/start.sh
37
- RUN echo 'bundle exec rackup -o 0.0.0.0 -p 9292' >> /home/start.sh
38
- RUN chmod +x /home/start.sh
37
+ RUN echo '#!/bin/sh' >> /usr/local/bin/start
38
+ RUN echo 'bundle exec rackup -o 0.0.0.0 -p 9292' >> /usr/local/bin/start
39
+ RUN chmod +x /usr/local/bin/start
39
40
 
40
41
  RUN echo '#!/bin/sh' >> /home/init-db.sh
41
42
  RUN echo 'bundle exec rake db:prepare:test' >> /home/init-db.sh
data/Gemfile CHANGED
@@ -36,3 +36,7 @@ end
36
36
  if ENV['INSTALL_PG'] == 'true'
37
37
  gem 'pg', '~>1.2'
38
38
  end
39
+
40
+ if ENV['X_PACT_DEVELOPMENT'] == 'true'
41
+ gem 'pact-support', path: '../pact-support'
42
+ end
@@ -0,0 +1,23 @@
1
+ # Issues
2
+
3
+ ## Reproducing an issue
4
+
5
+ In [script/reproduce-issue.rb](script/reproduce-issue.rb) you will find a fluent API that allows you to simulate client libraries interacting with the Pact Broker.
6
+
7
+ You can use it to easily reproduce issues.
8
+
9
+ To use it:
10
+
11
+ * Run the Pact Broker using the latest development code:
12
+
13
+ ```
14
+ docker-compose -f docker-compose-issue-repro.yml up --build pact-broker
15
+ ```
16
+
17
+ * Run the reproduction script.
18
+
19
+ ```
20
+ docker-compose -f docker-compose-issue-repro.yml up repro-issue
21
+ ```
22
+
23
+ You can modify `script/reproduce-issue.rb` and then re-run it with the change applied.
@@ -36,6 +36,20 @@ test:
36
36
  <<: *default
37
37
  adapter: mysql2
38
38
  host: "192.168.0.9"
39
+ github_actions_postgres:
40
+ adapter: postgres
41
+ database: postgres
42
+ username: postgres
43
+ password: postgres
44
+ host: localhost
45
+ port: "5432"
46
+ github_actions_mysql:
47
+ adapter: mysql2
48
+ database: pact_broker
49
+ username: pact_broker
50
+ password: pact_broker
51
+ host: localhost
52
+ port: 3306
39
53
 
40
54
  development:
41
55
  default:
@@ -2,17 +2,21 @@ version: "3"
2
2
 
3
3
  services:
4
4
  pact-broker:
5
- build:
6
- context: .
7
- dockerfile: issue-reproduction/Dockerfile-pact-broker
5
+ build: .
8
6
  ports:
9
7
  - "9292:9292"
10
- command: bundle exec rackup -p 9292 -o 0.0.0.0
8
+ entrypoint: /usr/local/bin/start
9
+ environment:
10
+ - RACK_ENV=production
11
+ volumes:
12
+ - $PWD:/home
11
13
 
12
14
  repro-issue:
13
- build:
14
- context: .
15
- dockerfile: issue-reproduction/Dockerfile-pact-broker
15
+ build: .
16
16
  depends_on:
17
17
  - pact-broker
18
18
  command: dockerize -wait http://pact-broker:9292 -timeout 30s /home/script/reproduce-issue.rb
19
+ environment:
20
+ - PACT_BROKER_BASE_URL=http://pact-broker:9292
21
+ volumes:
22
+ - $PWD:/home
@@ -0,0 +1,36 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/db/clean'
3
+ require 'pact_broker/matrix/unresolved_selector'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Resources
8
+ class Clean < BaseResource
9
+ def content_types_accepted
10
+ [["application/json"]]
11
+ end
12
+
13
+ def content_types_provided
14
+ [["application/hal+json"]]
15
+ end
16
+
17
+ def allowed_methods
18
+ ["POST", "OPTIONS"]
19
+ end
20
+
21
+ def process_post
22
+ keep_selectors = (params[:keep] || []).collect do | hash |
23
+ PactBroker::Matrix::UnresolvedSelector.new(hash)
24
+ end
25
+
26
+ result = PactBroker::DB::Clean.call(Sequel::Model.db, { keep: keep_selectors })
27
+ response.body = result.to_json
28
+ end
29
+
30
+ def policy_name
31
+ :'integrations::clean'
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -18,7 +18,11 @@ module PactBroker
18
18
  end
19
19
 
20
20
  def to_csv
21
- PactBroker::Api::Decorators::RelationshipsCsvDecorator.new(group).to_csv
21
+ if group
22
+ PactBroker::Api::Decorators::RelationshipsCsvDecorator.new(group).to_csv
23
+ else
24
+ ""
25
+ end
22
26
  end
23
27
 
24
28
  def policy_name
@@ -1,10 +1,13 @@
1
1
  require 'sequel'
2
2
  require 'pact_broker/project_root'
3
3
  require 'pact_broker/pacts/latest_tagged_pact_publications'
4
+ require 'pact_broker/logging'
4
5
 
5
6
  module PactBroker
6
7
  module DB
7
8
  class Clean
9
+ include PactBroker::Logging
10
+
8
11
 
9
12
  class Unionable < Array
10
13
  alias_method :union, :+
@@ -46,7 +49,7 @@ module PactBroker
46
49
  def latest_tagged_pact_publications_ids_to_keep
47
50
  @latest_tagged_pact_publications_ids_to_keep ||= resolve_ids(keep.select(&:tag).select(&:latest).collect do | selector |
48
51
  PactBroker::Pacts::LatestTaggedPactPublications.select(:id).for_selector(selector)
49
- end.reduce(&:union))
52
+ end.reduce(&:union) || [])
50
53
  end
51
54
 
52
55
 
@@ -0,0 +1,78 @@
1
+ require 'pact_broker/logging'
2
+ require 'pact_broker/matrix/unresolved_selector'
3
+
4
+ module PactBroker
5
+ module DB
6
+ class CleanIncremental
7
+ include PactBroker::Logging
8
+
9
+ DEFAULT_KEEP_SELECTORS = [
10
+ PactBroker::Matrix::UnresolvedSelector.new(tag: true, latest: true),
11
+ PactBroker::Matrix::UnresolvedSelector.new(latest: true),
12
+ PactBroker::Matrix::UnresolvedSelector.new(max_age: 90)
13
+ ]
14
+ TABLES = [:versions, :pact_publications, :pact_versions, :verifications, :triggered_webhooks, :webhook_executions]
15
+
16
+ def self.call database_connection, options = {}
17
+ new(database_connection, options).call
18
+ end
19
+
20
+ def initialize database_connection, options = {}
21
+ @db = database_connection
22
+ @options = options
23
+ end
24
+
25
+ def keep
26
+ options[:keep] || DEFAULT_KEEP_SELECTORS
27
+ end
28
+
29
+ def limit
30
+ options[:limit] || 1000
31
+ end
32
+
33
+ def resolve_ids(query, column_name = :id)
34
+ query.collect { |h| h[column_name] }
35
+ end
36
+
37
+ def version_ids_to_delete
38
+ db[:versions].where(id: version_ids_to_keep).invert.limit(limit).order(Sequel.asc(:id))
39
+ end
40
+
41
+ def version_ids_to_keep
42
+ @version_ids_to_keep ||= keep.collect do | selector |
43
+ PactBroker::Domain::Version.select(:id).for_selector(selector)
44
+ end.reduce(&:union)
45
+ end
46
+
47
+ def call
48
+ require 'pact_broker/domain/version'
49
+ before_counts = current_counts
50
+
51
+ result = PactBroker::Domain::Version.where(id: resolve_ids(version_ids_to_delete)).delete
52
+ delete_orphan_pact_versions
53
+
54
+ after_counts = current_counts
55
+
56
+ TABLES.each_with_object({}) do | table_name, comparison_counts |
57
+ comparison_counts[table_name.to_s] = { "deleted" => before_counts[table_name] - after_counts[table_name], "kept" => after_counts[table_name] }
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ attr_reader :db, :options
64
+
65
+ def current_counts
66
+ TABLES.each_with_object({}) do | table_name, counts |
67
+ counts[table_name] = db[table_name].count
68
+ end
69
+ end
70
+
71
+ def delete_orphan_pact_versions
72
+ logger.info("Deleting orphan pact versions")
73
+ referenced_pact_version_ids = db[:pact_publications].select(:pact_version_id).union(db[:verifications].select(:pact_version_id))
74
+ db[:pact_versions].where(id: referenced_pact_version_ids).invert.delete
75
+ end
76
+ end
77
+ end
78
+ end
@@ -2,27 +2,88 @@ require 'pact_broker/db'
2
2
  require 'pact_broker/repositories/helpers'
3
3
 
4
4
  module PactBroker
5
-
6
5
  module Domain
7
6
  class Tag < Sequel::Model
7
+ plugin :timestamps, update_on_create: true
8
+ plugin :insert_ignore, identifying_columns: [:name, :version_id]
9
+
10
+ unrestrict_primary_key
11
+ associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
8
12
 
9
13
  dataset_module do
10
14
  include PactBroker::Repositories::Helpers
11
- end
12
15
 
13
- plugin :insert_ignore, identifying_columns: [:name, :version_id]
16
+ # Does NOT care about whether or not there is a pact publication
17
+ # for the version
18
+ def latest_tags_for_pacticipant_ids(pacticipant_ids)
19
+ tags_versions_join = {
20
+ Sequel[:tags][:version_id] => Sequel[:versions][:id],
21
+ Sequel[:versions][:pacticipant_id] => pacticipant_ids
22
+ }
14
23
 
15
- unrestrict_primary_key
24
+ latest_tags_versions_join = {
25
+ Sequel[:latest_tags][:name] => Sequel[:tags][:name],
26
+ Sequel[:latest_tags][:latest_order] => Sequel[:versions][:order],
27
+ Sequel[:latest_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id],
28
+ Sequel[:versions][:pacticipant_id] => pacticipant_ids
29
+ }
16
30
 
17
- associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
31
+ latest_tags = PactBroker::Domain::Tag
32
+ .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id])
33
+ .select_append{ max(order).as(latest_order) }
34
+ .join(:versions, tags_versions_join)
35
+
36
+ PactBroker::Domain::Tag
37
+ .select_all_qualified
38
+ .join(:versions,
39
+ { Sequel[:tags][:version_id] => Sequel[:versions][:id],
40
+ Sequel[:versions][:pacticipant_id] => pacticipant_ids
41
+ })
42
+ .join(latest_tags, latest_tags_versions_join, { table_alias: :latest_tags })
43
+ end
44
+
45
+ def head_tags_for_consumer_id(consumer_id)
46
+ lp = :latest_pact_publication_ids_for_consumer_versions
47
+ tags_versions_join = {
48
+ Sequel[:tags][:version_id] => Sequel[:versions][:id],
49
+ Sequel[:versions][:pacticipant_id] => consumer_id
50
+ }
51
+
52
+ versions_pact_publications_join = {
53
+ Sequel[:versions][:id] => Sequel[lp][:consumer_version_id],
54
+ Sequel[lp][:consumer_id] => consumer_id
55
+ }
56
+ # head tags for this consumer
57
+ # the latest tag, pacticipant_id, version order
58
+ # for versions that have a pact publication
59
+ PactBroker::Domain::Tag
60
+ .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id])
61
+ .select_append{ max(order).as(latest_consumer_version_order) }
62
+ .join(:versions, tags_versions_join)
63
+ .join(lp, versions_pact_publications_join)
64
+ end
65
+
66
+ def head_tags_for_pact_publication(pact_publication)
67
+ head_tags_versions_join = {
68
+ Sequel[:head_tags][:latest_consumer_version_order] => Sequel[:versions][:order],
69
+ Sequel[:head_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id],
70
+ Sequel[:versions][:pacticipant_id] => pact_publication.consumer_id
71
+ }
72
+
73
+ # Find the head tags that belong to this pact publication
74
+ # Note: The tag model has the name and version_id,
75
+ # but does not have the created_at value set - but don't need it for now
76
+ head_tags_for_consumer_id(pact_publication.consumer_id).from_self(alias: :head_tags)
77
+ .select(Sequel[:head_tags][:name], Sequel[:versions][:id].as(:version_id))
78
+ .join(:versions, head_tags_versions_join)
79
+ .where(Sequel[:versions][:id] => pact_publication.consumer_version_id)
80
+ end
81
+ end
18
82
 
19
83
  def <=> other
20
84
  name <=> other.name
21
85
  end
22
-
23
86
  end
24
-
25
- Tag.plugin :timestamps, :update_on_create=>true
26
87
  end
27
88
  end
28
89