pact_broker 2.119.0 → 2.121.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +579 -479
  3. data/Gemfile +2 -6
  4. data/README.md +1 -2
  5. data/db/migrations/000015_create_pact_version_content.rb +1 -1
  6. data/db/migrations/000023_create_pact_versions_table.rb +1 -1
  7. data/db/migrations/000033_create_config_table.rb +1 -1
  8. data/db/migrations/000036_create_webhook_execution.rb +1 -1
  9. data/db/migrations/20180108_create_certificates_table.rb +1 -1
  10. data/db/migrations/migration_helper.rb +5 -1
  11. data/docs/CONFIGURATION.md +29 -2
  12. data/docs/matrix-optimisation/README.md +85 -0
  13. data/lib/pact/doc/interaction_view_model.rb +2 -1
  14. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +3 -1
  15. data/lib/pact/doc.rb +5 -0
  16. data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +2 -0
  17. data/lib/pact_broker/api/decorators/provider_states_decorator.rb +1 -1
  18. data/lib/pact_broker/api/decorators/triggered_webhook_logs_decorator.rb +4 -0
  19. data/lib/pact_broker/api/decorators/version_decorator.rb +29 -4
  20. data/lib/pact_broker/api/pact_broker_urls.rb +4 -0
  21. data/lib/pact_broker/api/resources/branch_versions.rb +5 -1
  22. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +4 -1
  23. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -0
  24. data/lib/pact_broker/api/resources/clean.rb +9 -1
  25. data/lib/pact_broker/api/resources/deployed_or_released_versions.rb +78 -0
  26. data/lib/pact_broker/api/resources/environment_versions.rb +89 -0
  27. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +29 -1
  28. data/lib/pact_broker/api/resources/tag_versions.rb +5 -1
  29. data/lib/pact_broker/api/resources/version.rb +5 -1
  30. data/lib/pact_broker/api/resources/versions.rb +5 -1
  31. data/lib/pact_broker/api.rb +2 -0
  32. data/lib/pact_broker/app.rb +0 -1
  33. data/lib/pact_broker/config/runtime_configuration.rb +19 -0
  34. data/lib/pact_broker/configuration.rb +15 -2
  35. data/lib/pact_broker/dataset.rb +4 -23
  36. data/lib/pact_broker/db/clean/branch_selector.rb +40 -0
  37. data/lib/pact_broker/db/clean.rb +45 -0
  38. data/lib/pact_broker/db/clean_incremental.rb +78 -14
  39. data/lib/pact_broker/db.rb +0 -7
  40. data/lib/pact_broker/deployments/released_version_service.rb +14 -0
  41. data/lib/pact_broker/domain/version.rb +6 -2
  42. data/lib/pact_broker/initializers/database_connection.rb +5 -0
  43. data/lib/pact_broker/integrations/repository.rb +1 -1
  44. data/lib/pact_broker/integrations/service.rb +1 -3
  45. data/lib/pact_broker/logging/open_telemetry_appender_setup.rb +73 -0
  46. data/lib/pact_broker/logging/trace_aware_json_formatter.rb +38 -0
  47. data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +29 -11
  48. data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +23 -12
  49. data/lib/pact_broker/matrix/parse_can_i_deploy_query.rb +5 -0
  50. data/lib/pact_broker/matrix/selector_resolver.rb +30 -2
  51. data/lib/pact_broker/pacticipants/repository.rb +5 -2
  52. data/lib/pact_broker/pacts/service.rb +2 -2
  53. data/lib/pact_broker/tags/service.rb +4 -5
  54. data/lib/pact_broker/tasks/clean_task.rb +23 -1
  55. data/lib/pact_broker/verifications/repository.rb +1 -1
  56. data/lib/pact_broker/version.rb +1 -1
  57. data/lib/pact_broker/versions/branch_repository.rb +1 -1
  58. data/lib/pact_broker/versions/branch_service.rb +1 -1
  59. data/lib/pact_broker/versions/branch_version_repository.rb +3 -2
  60. data/lib/pact_broker/versions/repository.rb +4 -13
  61. data/lib/pact_broker/webhooks/render.rb +1 -1
  62. data/lib/sequel/plugins/upsert.rb +1 -8
  63. data/pact_broker.gemspec +8 -7
  64. metadata +38 -32
data/Gemfile CHANGED
@@ -5,8 +5,6 @@ gemspec
5
5
 
6
6
  gem "rake", "~> 13.3"
7
7
  gem "sqlite3", ">=2.0.0"
8
- gem "conventional-changelog", "~>1.3"
9
- gem "bump", "~> 0.5"
10
8
  gem "padrino-core", ">= 0.16.0.pre3", require: false
11
9
  gem "rackup", "~> 2.2"
12
10
  gem "thor", "~> 1.4" # thor is secondary dependency but bumping here to avoid CVEs
@@ -21,6 +19,8 @@ end
21
19
 
22
20
  group :test do
23
21
  gem "simplecov", :require => false
22
+ gem "simplecov-cobertura", :require => false
23
+ gem "rspec_junit_formatter", :require => false
24
24
  if ENV["X_PACT_DEVELOPMENT"] == "true"
25
25
  gem "pact", path: "../pact-ruby"
26
26
  gem "pact-ffi", path: "../pact-ruby-ffi"
@@ -47,10 +47,6 @@ group :pg, optional: true do
47
47
  gem "pg", "~>1.6"
48
48
  end
49
49
 
50
- group :mysql, optional: true do
51
- gem "mysql2", "~>0.5"
52
- end
53
-
54
50
  if ENV["X_PACT_DEVELOPMENT"] == "true"
55
51
  gem "pact-support", path: "../pact-support"
56
52
  end
data/README.md CHANGED
@@ -151,7 +151,6 @@ You can use the [Pact Broker Docker image][docker] or [Terraform on AWS][terrafo
151
151
  * Are you sure you don't just want to use the [Pact Broker Docker image][docker]? No Docker at your company yet? Ah well, keep reading.
152
152
  * Create a PostgreSQL database.
153
153
  * To ensure you're on a supported version of the database that you choose, check the [.github/workflows/test.yml](.github/workflows/test.yml) file to see which versions we're currently running our tests against.
154
- * MySQL was supported for the native Ruby application until around 2021, but the official `pactfoundation/pact-broker` Docker image does not support it. New features will not be optimised for MySQL, and some new features may not even be supported on it (eg. the database clean feature).
155
154
  * You'll find a sample database creation script in the [example/config.ru](https://github.com/pact-foundation/pact_broker/blob/master/example/config.ru).
156
155
  * Install ruby 2.7 and the latest version of bundler (if you've come this far, I'm assuming you know how to do both of these. Did I mention there was a [Docker][docker] image?)
157
156
  * Copy the [pact\_broker](https://github.com/DiUS/pact_broker-docker/tree/master/pact_broker) directory from the Pact Broker Docker project. This will have the recommended settings for database connections, logging, basic auth etc. Note that the Docker image uses Phusion Passenger as the web application server in front of the Pact Broker Ruby application, which is the recommended set up if you are not using a containerized solution.
@@ -170,7 +169,7 @@ Please read the [UPGRADING.md](UPGRADING.md) documentation before upgrading your
170
169
 
171
170
  The Pact Broker follows the [semantic versioning](https://semver.org/) scheme.
172
171
 
173
- [decouple]: https://www.rea-group.com/blog/enter-the-pact-matrix-or-how-to-decouple-the-release-cycles-of-your-microservices/
172
+ [decouple]: https://docs.pact.io/pact_broker/can_i_deploy
174
173
  [pact]: https://github.com/pact-foundation/pact-ruby
175
174
  [nerf]: https://github.com/pact-foundation/pact_broker/wiki/pact-broker-ci-nerf-gun
176
175
  [different-teams]: https://github.com/pact-foundation/pact-ruby/wiki/Using-pact-where-the-consumer-team-is-different-from-the-provider-team
@@ -5,7 +5,7 @@ Sequel.migration do
5
5
  change do
6
6
  create_table(:pact_version_contents, charset: "utf8") do
7
7
  String :sha, primary_key: true, null: false, primary_key_constraint_name: "pk_pact_version_contents"
8
- String :content, type: PactBroker::MigrationHelper.large_text_type
8
+ column :content, PactBroker::MigrationHelper.large_text_type
9
9
  DateTime :created_at, null: false
10
10
  DateTime :updated_at, null: false
11
11
  end
@@ -7,7 +7,7 @@ Sequel.migration do
7
7
  foreign_key :consumer_id, :pacticipants
8
8
  foreign_key :provider_id, :pacticipants
9
9
  String :sha, null: false
10
- String :content, type: PactBroker::MigrationHelper.large_text_type
10
+ column :content, PactBroker::MigrationHelper.large_text_type
11
11
  index [:consumer_id, :provider_id, :sha], unique: true, name: "unq_pvc_con_prov_sha"
12
12
  DateTime :created_at, null: false
13
13
  end
@@ -6,7 +6,7 @@ Sequel.migration do
6
6
  primary_key :id
7
7
  String :name, null: false
8
8
  String :type, null: false
9
- String :value, type: PactBroker::MigrationHelper.large_text_type
9
+ column :value, PactBroker::MigrationHelper.large_text_type
10
10
  DateTime :created_at, null: false
11
11
  DateTime :updated_at, null: false
12
12
  index [:name], unique: true, name: "unq_config_name"
@@ -9,7 +9,7 @@ Sequel.migration do
9
9
  foreign_key :consumer_id, :pacticipants, null: false
10
10
  foreign_key :provider_id, :pacticipants, null: false
11
11
  Boolean :success, null: false
12
- String :logs, type: PactBroker::MigrationHelper.large_text_type
12
+ column :logs, PactBroker::MigrationHelper.large_text_type
13
13
  DateTime :created_at, null: false
14
14
  end
15
15
  end
@@ -6,7 +6,7 @@ Sequel.migration do
6
6
  primary_key :id
7
7
  String :uuid, null: false, unique: true, unique_constraint_name: "uq_certificate_uuid"
8
8
  String :description, null: true
9
- String :content, null: false, type: PactBroker::MigrationHelper.large_text_type
9
+ column :content, PactBroker::MigrationHelper.large_text_type, null: false
10
10
  DateTime :created_at, null: false
11
11
  DateTime :updated_at, null: false
12
12
  end
@@ -10,7 +10,7 @@ module PactBroker
10
10
  end
11
11
 
12
12
  def large_text_type
13
- if postgres?
13
+ if postgres? || sqlite?
14
14
  :text
15
15
  else
16
16
  # Assume mysql
@@ -32,6 +32,10 @@ module PactBroker
32
32
  adapter =~ /postgres/
33
33
  end
34
34
 
35
+ def sqlite?
36
+ adapter =~ /sqlite/
37
+ end
38
+
35
39
  def adapter
36
40
  Sequel::Model.db.adapter_scheme.to_s
37
41
  end
@@ -34,6 +34,25 @@ The application log format. Can be any value supported by Semantic Logger.
34
34
  **Allowed values:** `default`, `json`, `color`<br/>
35
35
  **More information:** https://github.com/rocketjob/semantic_logger/tree/master/lib/semantic_logger/formatters<br/>
36
36
 
37
+ ### log_otel_enabled
38
+
39
+ Controls whether logs are also emitted to the OpenTelemetry logs pipeline.
40
+
41
+ `auto` (the default) adds the OpenTelemetry appender only when the
42
+ `opentelemetry-logs-sdk` gem is installed and an OpenTelemetry logger provider
43
+ is configured. `true` forces the appender on and raises an error if the gem is
44
+ not available. `false` disables it entirely.
45
+
46
+ The `opentelemetry-logs-sdk` gem is an optional, runtime-detected dependency and
47
+ is not bundled with the Pact Broker. When `log_format` is set to `json`, active
48
+ trace and span IDs are also embedded in the JSON log output.
49
+
50
+ **Supported versions:** From v2.120.0<br/>
51
+ **Environment variable name:** `PACT_BROKER_LOG_OTEL_ENABLED`<br/>
52
+ **YAML configuration key name:** `log_otel_enabled`<br/>
53
+ **Default:** `auto`<br/>
54
+ **Allowed values:** `auto`, `true`, `false`<br/>
55
+
37
56
  ### log_dir
38
57
 
39
58
  The log file directory
@@ -248,8 +267,8 @@ The number of seconds after which an SQL query will be aborted. Only supported f
248
267
 
249
268
  **Environment variable name:** `PACT_BROKER_DATABASE_STATEMENT_TIMEOUT`<br/>
250
269
  **YAML configuration key name:** `database_statement_timeout`<br/>
251
- **Default:** `15000`<br/>
252
- **Allowed values:** A positive integer in milliseconds<br/>
270
+ **Default:** `15`<br/>
271
+ **Allowed values:** A positive integer or float.<br/>
253
272
  **More information:** https://www.postgresql.org/docs/9.3/runtime-config-client.html<br/>
254
273
 
255
274
  ### metrics_sql_statement_timeout
@@ -533,6 +552,14 @@ The URL of the shields.io server used to generate the README badges.
533
552
  **Default:** `https://img.shields.io`<br/>
534
553
  **More information:** https://shields.io<br/>
535
554
 
555
+ ### badge_default_cache_setting
556
+
557
+ Cache Control header value for the badge, this sets the cache-control header for the badge image, for more information on header formats see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/cache-control
558
+
559
+ **Environment variable name:** `PACT_BROKER_BADGE_DEFAULT_CACHE_SETTING`<br/>
560
+ **YAML configuration key name:** `badge_default_cache_setting`<br/>
561
+ **Default:** `max-age=30`<br/>
562
+
536
563
  ### badge_provider_mode
537
564
 
538
565
  The method by which the badges are generated. When set to `redirect`, a request to the Pact Broker for a badge will be sent a redirect response
@@ -0,0 +1,85 @@
1
+ # Matrix query baselines
2
+
3
+ Tooling to capture a query-profile baseline (per-request query count, exact SQL,
4
+ and `EXPLAIN` plans) for the `/matrix` and `/can-i-deploy` endpoints, so query
5
+ cost can be compared before and after changes to the query engine.
6
+
7
+ The generated baseline files (`baseline-*.md`) are **git-ignored** — generate them
8
+ locally and diff two runs; they are not committed.
9
+
10
+ ## Generate a baseline (Postgres)
11
+
12
+ Baselines are generated against **Postgres** (the production backend). Start a
13
+ throwaway Postgres with the dev compose file:
14
+
15
+ docker compose -f docker-compose-dev-postgres.yml up -d postgres
16
+
17
+ The Postgres version is controlled by `POSTGRES_VERSION` (default `15`),
18
+ so a baseline can be captured against a specific major version:
19
+
20
+ POSTGRES_VERSION=14 docker compose -f docker-compose-dev-postgres.yml up -d postgres
21
+
22
+ The `pg` driver lives in an optional bundle group, so it must be installed and
23
+ enabled explicitly — without `BUNDLE_WITH=pg` the run fails with
24
+ `LoadError: cannot load such file -- pg`:
25
+
26
+ BUNDLE_WITH=pg bundle install
27
+
28
+ Point the suite at the database and run the generator:
29
+
30
+ BUNDLE_WITH=pg \
31
+ PACT_BROKER_TEST_DATABASE_URL="postgres://postgres:postgres@localhost:5432/postgres" \
32
+ bundle exec rspec spec/benchmarks/matrix_baseline_spec.rb --tag matrix_baseline
33
+
34
+ This writes `docs/matrix-optimisation/baseline-postgres.md`. Tear the container down
35
+ with `docker compose -f docker-compose-dev-postgres.yml down -v` when done.
36
+
37
+ > Postgres 18 and later relocated the data directory, which is incompatible with
38
+ > the `/var/lib/postgresql/data` volume mount in `docker-compose-dev-postgres.yml`;
39
+ > the container exits on startup. Use a tag of `17` or lower until that mount is
40
+ > updated.
41
+
42
+ The generator is tagged `matrix_baseline` and excluded from the default test run;
43
+ it only executes when invoked with `--tag matrix_baseline`. Against a non-Postgres
44
+ database it skips with a message.
45
+
46
+ ## What's captured
47
+
48
+ For each request shape (see `spec/support/matrix_baseline/shapes.rb`), the file
49
+ records, inside collapsible sections:
50
+
51
+ - the number of SQL statements the request issued (main query + eager loads);
52
+ - the exact SQL of each statement;
53
+ - its `EXPLAIN` plan.
54
+
55
+ Plans are captured with `EXPLAIN (ANALYZE, BUFFERS, TIMING OFF)`. That keeps the
56
+ measures of work done — estimated cost, actual row counts, buffer hit/read
57
+ counts, sort and hash memory usage — and drops the measures of how fast this
58
+ particular machine did it. Wall-clock figures are excluded deliberately: they
59
+ vary with unrelated load and would swamp the diff.
60
+
61
+ ## Comparing runs
62
+
63
+ **Two runs against unchanged code produce a byte-identical file.** Any diff at
64
+ all is a real difference in what the query engine did. Regenerate and diff two
65
+ `baseline-postgres.md` files to compare:
66
+
67
+ - **query count** per shape (the `N queries` in each section heading);
68
+ - **plan shape** — sequential vs index scans, sorts, join types, UNION branch count;
69
+ - **estimated cost, actual rows, buffers and memory usage** per plan node.
70
+
71
+ Determinism depends on the generator resetting the database itself — it
72
+ truncates with `RESTART IDENTITY` so surrogate keys in the captured SQL are
73
+ stable, and runs `VACUUM ANALYZE` so plans are chosen against real statistics
74
+ on an unbloated heap. That is why it is tagged `no_db_clean` and will wipe the
75
+ database it is pointed at. **Point it at a throwaway Postgres, never at a
76
+ database whose contents you want to keep.**
77
+
78
+ ## Limitations
79
+
80
+ - The seed is a few hundred rows per table. Postgres will often choose a
81
+ sequential scan at this size regardless of index quality, so a plan-shape
82
+ change here does not automatically imply the same change in production. Query
83
+ counts and join structure transfer; scan choice on small tables may not.
84
+ - The success filter and can-i-deploy's `--ignore` are applied in Ruby after
85
+ the query, so their shapes exercise no extra SQL beyond selector resolution.
@@ -1,6 +1,7 @@
1
1
  require "pact/shared/active_support_support"
2
2
  require "pact/reification"
3
3
  require "cgi"
4
+ require "pact/doc"
4
5
 
5
6
  module Pact
6
7
  module Doc
@@ -149,7 +150,7 @@ module Pact
149
150
 
150
151
  def markdown_escape string
151
152
  return nil unless string
152
- string.gsub("*","\\*").gsub("_","\\_")
153
+ string.gsub(Pact::Doc::MARKDOWN_SPECIAL_CHARS_REGEXP) { |char| "\\#{char}" }
153
154
  end
154
155
  end
155
156
  end
@@ -1,6 +1,7 @@
1
1
  require "pact/doc/markdown/interaction_renderer"
2
2
  require "pact/doc/sort_interactions"
3
3
  require "rack/utils"
4
+ require "pact/doc"
4
5
 
5
6
  module Pact
6
7
  module Doc
@@ -60,7 +61,8 @@ module Pact
60
61
  end
61
62
 
62
63
  def markdown_escape string
63
- string.gsub("*","\\*").gsub("_","\\_")
64
+ return nil unless string
65
+ string.gsub(Pact::Doc::MARKDOWN_SPECIAL_CHARS_REGEXP) { |char| "\\#{char}" }
64
66
  end
65
67
 
66
68
  def h(text)
data/lib/pact/doc.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Pact
2
+ module Doc
3
+ MARKDOWN_SPECIAL_CHARS_REGEXP = /[\\`*_\[\](){}#+\-.!|]/
4
+ end
5
+ end
@@ -10,6 +10,8 @@ module PactBroker
10
10
  required(:name_in_pact).maybe(:string)
11
11
  end
12
12
 
13
+ rule(:name).validate(:not_blank_if_present)
14
+
13
15
  rule(:name, :name_in_pact) do
14
16
  if name_in_pact_does_not_match_name_in_url_path?(values)
15
17
  key.failure(validation_message("pact_name_in_path_mismatch_name_in_pact", name_in_pact: values[:name_in_pact], name_in_path: values[:name]))
@@ -26,7 +26,7 @@ module PactBroker
26
26
  end
27
27
 
28
28
  consumers_map.map do |(name, params), consumers|
29
- { name: name, params: params, consumers: consumers.uniq }.transform_keys(&:to_sym)
29
+ { name: name, params: params, consumers: consumers.uniq.sort }
30
30
  end
31
31
  .sort_by { |provider_state| provider_state[:name] }
32
32
  }, :extend => PactBroker::Api::Decorators::ProviderStateDecorator
@@ -11,6 +11,10 @@ module PactBroker
11
11
  end
12
12
 
13
13
 
14
+ property :event_name, as: :eventName
15
+ property :consumer_name, as: :consumerName
16
+ property :provider_name, as: :providerName
17
+
14
18
  nested :triggeredWebhook, embedded: true do
15
19
  property :uuid
16
20
  end
@@ -1,6 +1,7 @@
1
1
  require_relative "base_decorator"
2
2
  require_relative "embedded_tag_decorator"
3
3
  require_relative "embedded_branch_version_decorator"
4
+ require_relative "format_date_time"
4
5
 
5
6
  module PactBroker
6
7
  module Api
@@ -74,21 +75,45 @@ module PactBroker
74
75
  end
75
76
 
76
77
  links :'pb:deployed-environments' do | context |
77
- # I couldn't another way to check if call is from a collection or a single item 🤷‍♂️
78
- # Also not sure why the heck .to_a?(Hash) doesn't work, I mean what...
79
- deployed_versions = context[:deployed_versions].class.to_s == "Hash" ? context[:deployed_versions][represented.id] : context[:deployed_versions]
78
+ # NB: use fully-qualified ::Hash. The tins gem shadows the bare `Hash`
79
+ # constant in this scope, so `is_a?(Hash)` returns false even for a real
80
+ # Hash (this is the collection-vs-single-item branch: the collection
81
+ # resource passes a version_id-keyed Hash, the single resource an Array).
82
+ deployed_versions = context.dig(:deployed_versions)
83
+ deployed_versions = deployed_versions[represented.id] if deployed_versions.is_a?(::Hash)
80
84
  deployed_versions&.collect do | deployed_version |
81
85
  {
82
86
  title: "Version deployed to #{deployed_version.environment.display_name}",
83
87
  name: deployed_version.environment.display_name,
84
88
  href: deployed_version_url(deployed_version, context.fetch(:base_url)),
85
- currently_deployed: deployed_version.currently_deployed
89
+ currently_deployed: deployed_version.currently_deployed,
90
+ deployed_at: FormatDateTime.call(deployed_version.created_at),
91
+ undeployed_at: FormatDateTime.call(deployed_version.undeployed_at)
86
92
  }.tap do |hash|
87
93
  hash[:application_instance] = deployed_version.application_instance unless deployed_version.application_instance.nil?
88
94
  end
89
95
  end
90
96
  end
91
97
 
98
+ links :'pb:released-environments' do | context |
99
+ # NB: use fully-qualified ::Hash. The tins gem shadows the bare `Hash`
100
+ # constant in this scope, so `is_a?(Hash)` returns false even for a real
101
+ # Hash (this is the collection-vs-single-item branch: the collection
102
+ # resource passes a version_id-keyed Hash, the single resource an Array).
103
+ released_versions = context.dig(:released_versions)
104
+ released_versions = released_versions[represented.id] if released_versions.is_a?(::Hash)
105
+ released_versions&.collect do | released_version |
106
+ {
107
+ title: "Version released to #{released_version.environment.display_name}",
108
+ name: released_version.environment.display_name,
109
+ href: released_version_url(released_version, context.fetch(:base_url)),
110
+ currently_supported: released_version.currently_supported,
111
+ released_at: FormatDateTime.call(released_version.created_at),
112
+ support_ended_at: FormatDateTime.call(released_version.support_ended_at)
113
+ }
114
+ end
115
+ end
116
+
92
117
  links :'pb:record-deployment' do | context |
93
118
  context[:environments]&.collect do | environment |
94
119
  {
@@ -370,6 +370,10 @@ module PactBroker
370
370
  "#{environments_url(base_url)}/#{environment.uuid}"
371
371
  end
372
372
 
373
+ def environment_versions_url(pacticipant, environment, base_url = "")
374
+ "#{pacticipant_url(base_url, pacticipant)}/environments/#{environment.uuid}/versions"
375
+ end
376
+
373
377
  def deployed_versions_for_version_and_environment_url(version, environment, base_url = "")
374
378
  "#{version_url(base_url, version)}/deployed-versions/environment/#{environment.uuid}"
375
379
  end
@@ -27,7 +27,7 @@ module PactBroker
27
27
  end
28
28
 
29
29
  def to_json
30
- decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(resource_title: resource_title, deployed_versions: deployed_versions)))
30
+ decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(resource_title: resource_title, deployed_versions: deployed_versions, released_versions: released_versions)))
31
31
  end
32
32
 
33
33
  def versions
@@ -38,6 +38,10 @@ module PactBroker
38
38
  @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(versions)
39
39
  end
40
40
 
41
+ def released_versions
42
+ @released_versions ||= released_version_service.find_released_versions_for_versions(versions)
43
+ end
44
+
41
45
  def policy_name
42
46
  :'versions::versions'
43
47
  end
@@ -37,8 +37,11 @@ module PactBroker
37
37
  end
38
38
 
39
39
  def options
40
+ # "cvpv" (not "cvp") so that every version of an integrated application that is currently
41
+ # released/deployed to the environment is evaluated independently, rather than collapsing
42
+ # to just the latest provider version (which hides still-live incompatible versions). See issue #903.
40
43
  @options ||= {
41
- latestby: "cvp",
44
+ latestby: "cvpv",
42
45
  environment_name: identifier_from_path[:environment_name]
43
46
  }
44
47
  end
@@ -1,4 +1,5 @@
1
1
  require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment"
2
+ require "pact_broker/api/resources/badge_methods"
2
3
 
3
4
  module PactBroker
4
5
  module Api
@@ -1,5 +1,6 @@
1
1
  require "pact_broker/api/resources/base_resource"
2
2
  require "pact_broker/db/clean"
3
+ require "pact_broker/db/clean/branch_selector"
3
4
  require "pact_broker/matrix/unresolved_selector"
4
5
 
5
6
  # Not exposed yet as we'd need to support administrator auth first
@@ -22,7 +23,14 @@ module PactBroker
22
23
  PactBroker::DB::Clean::Selector.new(hash)
23
24
  end
24
25
 
25
- result = PactBroker::DB::Clean.call(Sequel::Model.db, { keep: keep_selectors })
26
+ keep_branch_selectors = (params[:keep_branches] || []).collect do | hash |
27
+ PactBroker::DB::Clean::BranchSelector.from_hash(hash)
28
+ end
29
+
30
+ result = PactBroker::DB::Clean.call(Sequel::Model.db, {
31
+ keep: keep_selectors,
32
+ keep_branches: keep_branch_selectors.empty? ? nil : keep_branch_selectors
33
+ })
26
34
  response.body = result.to_json
27
35
  else
28
36
  415
@@ -0,0 +1,78 @@
1
+ require "pact_broker/api/resources/base_resource"
2
+ require "pact_broker/api/decorators/versions_decorator"
3
+ require "pact_broker/api/resources/pagination_methods"
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Resources
8
+ class DeployedOrReleasedVersions < BaseResource
9
+ include PaginationMethods
10
+
11
+ def content_types_provided
12
+ [["application/hal+json", :to_json]]
13
+ end
14
+
15
+ def allowed_methods
16
+ ["GET", "OPTIONS"]
17
+ end
18
+
19
+ def malformed_request?
20
+ super || request.get? && validation_errors_for_schema?(schema, request.query)
21
+ end
22
+
23
+ def resource_exists?
24
+ !!pacticipant
25
+ end
26
+
27
+ def to_json
28
+ decorator_class(:versions_decorator).new(versions).to_json(
29
+ **decorator_options(
30
+ identifier_from_path.merge(
31
+ resource_title: "Versions of #{pacticipant_name} that have been deployed or released",
32
+ deployed_versions: deployed_versions,
33
+ released_versions: released_versions
34
+ )
35
+ )
36
+ )
37
+ end
38
+
39
+ def policy_name
40
+ :'versions::versions'
41
+ end
42
+
43
+ private
44
+
45
+ def versions
46
+ @versions ||= version_service.find_by_ids_in_reverse_order(
47
+ version_ids,
48
+ pagination_options,
49
+ decorator_class(:versions_decorator).eager_load_associations
50
+ )
51
+ end
52
+
53
+ def version_ids
54
+ PactBroker::Deployments::DeployedVersion
55
+ .for_pacticipant_name(pacticipant_name)
56
+ .select(:version_id)
57
+ .union(
58
+ PactBroker::Deployments::ReleasedVersion
59
+ .for_pacticipant_name(pacticipant_name)
60
+ .select(:version_id)
61
+ )
62
+ end
63
+
64
+ def deployed_versions
65
+ @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(versions)
66
+ end
67
+
68
+ def released_versions
69
+ @released_versions ||= released_version_service.find_released_versions_for_versions(versions)
70
+ end
71
+
72
+ def schema
73
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema if request.get?
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,89 @@
1
+ require "pact_broker/api/resources/base_resource"
2
+ require "pact_broker/api/decorators/versions_decorator"
3
+ require "pact_broker/api/resources/pagination_methods"
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Resources
8
+ class EnvironmentVersions < BaseResource
9
+ include PaginationMethods
10
+
11
+ def content_types_provided
12
+ [["application/hal+json", :to_json]]
13
+ end
14
+
15
+ def allowed_methods
16
+ ["GET", "OPTIONS"]
17
+ end
18
+
19
+ def malformed_request?
20
+ super || request.get? && validation_errors_for_schema?(schema, request.query)
21
+ end
22
+
23
+ def resource_exists?
24
+ !!environment && !!pacticipant
25
+ end
26
+
27
+ def to_json
28
+ decorator_class(:versions_decorator).new(versions).to_json(
29
+ **decorator_options(
30
+ identifier_from_path.merge(
31
+ resource_title: resource_title,
32
+ deployed_versions: deployed_versions,
33
+ released_versions: released_versions
34
+ )
35
+ )
36
+ )
37
+ end
38
+
39
+ def policy_name
40
+ :'versions::versions'
41
+ end
42
+
43
+ private
44
+
45
+ def versions
46
+ @versions ||= version_service.find_by_ids_in_reverse_order(
47
+ version_ids,
48
+ pagination_options,
49
+ decorator_class(:versions_decorator).eager_load_associations
50
+ )
51
+ end
52
+
53
+ def version_ids
54
+ deployed_ids = PactBroker::Deployments::DeployedVersion
55
+ .for_environment(environment)
56
+ .for_pacticipant_name(pacticipant_name)
57
+ .select_map(:version_id)
58
+
59
+ released_ids = PactBroker::Deployments::ReleasedVersion
60
+ .for_environment(environment)
61
+ .for_pacticipant_name(pacticipant_name)
62
+ .select_map(:version_id)
63
+
64
+ (deployed_ids + released_ids).uniq
65
+ end
66
+
67
+ def deployed_versions
68
+ @deployed_versions ||= deployed_version_service.find_deployed_versions_for_versions(versions)
69
+ end
70
+
71
+ def released_versions
72
+ @released_versions ||= released_version_service.find_released_versions_for_versions(versions)
73
+ end
74
+
75
+ def environment
76
+ @environment ||= environment_service.find(identifier_from_path[:environment_uuid])
77
+ end
78
+
79
+ def resource_title
80
+ "Versions for #{pacticipant.name} in environment #{environment.display_name}"
81
+ end
82
+
83
+ def schema
84
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema if request.get?
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end