pact_broker 2.108.0 → 2.109.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79ce9d64a823a800533d3312f75b9e008d621833683e1f1424827fdf146ae855
4
- data.tar.gz: 46c802480921553348daf0f3db5af2cbd94dd0a3b40de7db29f7137846768b93
3
+ metadata.gz: a915f7f2cb962cfc2d315338305f170cc875746ce0567826b54a8245dc06afa9
4
+ data.tar.gz: 3f2db5d2e4c1a5ece6b6b5fef712f3bbcaf85eae49856b9309632ff525d23173
5
5
  SHA512:
6
- metadata.gz: 2fc7c186cd8b1174668c97724e9065bf417768ec854726d3651a1ad1c13908adaa6ad36979f17be05483208fab304dad25f19dc8e31c085c685fc73c9539192e
7
- data.tar.gz: bbb31b5fde13172358084bd35e3d52161a4440f91c2363ed337f7aa5512e8669cc39a323ae7f970016924a669de4eedb35d891a3ce95cadea850032a97c995dd
6
+ metadata.gz: 4cc5845f6611967e5a33865b05da2a214d8fa2d28762943ea4f112bab8a02163830acde551342486cb2b03690564b8a33eb3e5242afd93bc5e70249c1416ca56
7
+ data.tar.gz: 12f531f2a74c399bc48a26b7afb82e68453b35764c3f5aca5434a1fc55c9f377590508ea6ee41840547652ce240cce2dc4d244d88e4fc2b4074b8c8a8f441dfb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ <a name="v2.109.0"></a>
2
+ ### v2.109.0 (2024-02-01)
3
+
4
+ #### Features
5
+
6
+ * use SemanticLogger for Padrino (#662) ([5d9d7002](/../../commit/5d9d7002))
7
+ * improve performance of publication for very large pacts by calculating the content SHA only once per request ([a947e409](/../../commit/a947e409))
8
+
9
+ #### Bug Fixes
10
+
11
+ * pass in environment to environment policy when getting an individual environment ([5c386a43](/../../commit/5c386a43))
12
+ * Dockerfile to reduce vulnerabilities (#650) ([9aaa3484](/../../commit/9aaa3484))
13
+
1
14
  <a name="v2.108.0"></a>
2
15
  ### v2.108.0 (2024-01-05)
3
16
 
data/Gemfile CHANGED
@@ -2,8 +2,6 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- # While https://github.com/brandonhilkert/sucker_punch/pull/253 is outstanding
6
- gem "sucker_punch", git: "https://github.com/pact-foundation/sucker_punch.git", ref: "fix/rename-is-singleton-class-method-2"
7
5
 
8
6
  gem "rake", "~>12.3.3"
9
7
  gem "sqlite3", "~>1.3"
@@ -11,8 +11,13 @@ module PactBroker
11
11
  property :provider_name
12
12
  property :specification
13
13
  property :content_type
14
- property :decoded_content
14
+ property :decoded_content, setter: -> (fragment:, represented:, user_options:, **) {
15
+ represented.decoded_content = fragment
16
+ # Set the pact version sha when we set the content
17
+ represented.pact_version_sha = user_options.fetch(:sha_generator).call(fragment)
18
+ }
15
19
  property :on_conflict, default: "overwrite"
20
+
16
21
  end
17
22
  end
18
23
  end
@@ -16,7 +16,7 @@ module PactBroker
16
16
  property :name
17
17
  end
18
18
 
19
- property :uuid
19
+ property :uuid, writeable: false
20
20
 
21
21
  property :description, getter: lambda { |represented:, **| represented.display_description }
22
22
 
@@ -42,6 +42,10 @@ module PactBroker
42
42
  :'deployments::environment'
43
43
  end
44
44
 
45
+ def policy_record
46
+ environment
47
+ end
48
+
45
49
  def to_json
46
50
  decorator_class(:environment_decorator).new(environment).to_json(**decorator_options)
47
51
  end
@@ -71,9 +71,9 @@ module PactBroker
71
71
  subscribe(PactBroker::Integrations::EventListener.new) do
72
72
  handle_webhook_events do
73
73
  if request.patch? && resource_exists?
74
- @pact = pact_service.merge_pact(pact_params)
74
+ @pact = pact_service.merge_pact(pact_params.merge(pact_version_sha: pact_version_sha))
75
75
  else
76
- @pact = pact_service.create_or_update_pact(pact_params)
76
+ @pact = pact_service.create_or_update_pact(pact_params.merge(pact_version_sha: pact_version_sha))
77
77
  end
78
78
  end
79
79
  end
@@ -114,7 +114,7 @@ module PactBroker
114
114
  end
115
115
 
116
116
  def disallowed_modification?
117
- if request.really_put? && pact_service.disallowed_modification?(pact, pact_params.json_content)
117
+ if request.really_put? && pact_service.disallowed_modification?(pact, pact_version_sha)
118
118
  message_params = { consumer_name: pact_params.consumer_name, consumer_version_number: pact_params.consumer_version_number, provider_name: pact_params.provider_name }
119
119
  set_json_error_message(message("errors.validation.pact_content_modification_not_allowed", message_params))
120
120
  true
@@ -126,6 +126,10 @@ module PactBroker
126
126
  def schema
127
127
  api_contract_class(:put_pact_params_contract)
128
128
  end
129
+
130
+ def pact_version_sha
131
+ @pact_version_sha ||= pact_service.generate_sha(pact_params.json_content)
132
+ end
129
133
  end
130
134
  end
131
135
  end
@@ -53,7 +53,7 @@ module PactBroker
53
53
  private
54
54
 
55
55
  def parsed_contracts
56
- @parsed_contracts ||= decorator_class(:publish_contracts_decorator).new(PactBroker::Contracts::ContractsToPublish.new).from_hash(params)
56
+ @parsed_contracts ||= decorator_class(:publish_contracts_decorator).new(PactBroker::Contracts::ContractsToPublish.new).from_hash(params, { user_options: { sha_generator: PactBroker.configuration.sha_generator } } )
57
57
  end
58
58
 
59
59
  def params
@@ -1,8 +1,3 @@
1
- # Must be defined before loading Padrino
2
- PADRINO_LOGGER ||= {
3
- ENV.fetch("RACK_ENV", "production").to_sym => { log_level: :error, stream: :stdout, format_datetime: "%Y-%m-%dT%H:%M:%S.000%:z" }
4
- }
5
-
6
1
  require "pact_broker/configuration"
7
2
  require "pact_broker/db"
8
3
  require "pact_broker/initializers/database_connection"
@@ -1,11 +1,10 @@
1
1
  module PactBroker
2
2
  module Contracts
3
- ContractToPublish = Struct.new(:consumer_name, :provider_name, :decoded_content, :content_type, :specification, :on_conflict) do
4
- # rubocop: disable Metrics/ParameterLists
5
- def self.from_hash(consumer_name: nil, provider_name: nil, decoded_content: nil, content_type: nil, specification: nil, on_conflict: nil)
6
- new(consumer_name, provider_name, decoded_content, content_type, specification, on_conflict)
3
+ ContractToPublish = Struct.new(:consumer_name, :provider_name, :decoded_content, :content_type, :specification, :on_conflict, :pact_version_sha, keyword_init: true) do
4
+
5
+ def self.from_hash(hash)
6
+ new(**hash)
7
7
  end
8
- # rubocop: enable Metrics/ParameterLists
9
8
 
10
9
  def pact?
11
10
  specification == "pact"
@@ -57,7 +57,7 @@ module PactBroker
57
57
  parsed_contracts.contracts.collect do | contract_to_publish |
58
58
  pact_params = create_pact_params(parsed_contracts, contract_to_publish)
59
59
  existing_pact = pact_service.find_pact(pact_params)
60
- if existing_pact && pact_service.disallowed_modification?(existing_pact, contract_to_publish.decoded_content)
60
+ if existing_pact && pact_service.disallowed_modification?(existing_pact, contract_to_publish.pact_version_sha)
61
61
  add_pact_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_pact.json_content, contract_to_publish.decoded_content)
62
62
  end
63
63
  end
@@ -134,7 +134,7 @@ module PactBroker
134
134
  pact_params = create_pact_params(parsed_contracts, contract_to_publish)
135
135
  existing_pact = pact_service.find_pact(pact_params)
136
136
  listener = TriggeredWebhooksCreatedListener.new
137
- created_pact = create_or_merge_pact(contract_to_publish.merge?, existing_pact, pact_params, listener)
137
+ created_pact = create_or_merge_pact(contract_to_publish.merge?, existing_pact, pact_params.merge(pact_version_sha: contract_to_publish.pact_version_sha), listener)
138
138
  notices.concat(notices_for_pact(parsed_contracts, contract_to_publish, existing_pact, created_pact, listener, base_url))
139
139
  created_pact
140
140
  end
@@ -42,6 +42,16 @@ module PactBroker
42
42
  end
43
43
  end
44
44
 
45
+ def measure_info(message, payload: {})
46
+ if logger.respond_to?(:measure_info)
47
+ logger.measure_info(message, payload: payload) do
48
+ yield
49
+ end
50
+ else
51
+ yield
52
+ end
53
+ end
54
+
45
55
  def log_error e, description = nil
46
56
  if logger.instance_of?(SemanticLogger::Logger)
47
57
  if description
@@ -3,22 +3,27 @@ require "pact_broker/configuration"
3
3
  require "pact_broker/pacts/sort_content"
4
4
  require "pact_broker/pacts/parse"
5
5
  require "pact_broker/pacts/content"
6
+ require "pact_broker/logging"
6
7
 
7
8
  module PactBroker
8
9
  module Pacts
9
10
  class GenerateSha
11
+ include PactBroker::Logging
12
+
10
13
  # @param [String] json_content
11
- def self.call json_content, _options = {}
14
+ def self.call(json_content, _options = {})
12
15
  content_for_sha = if PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results
13
16
  extract_verifiable_content_for_sha(json_content)
14
17
  else
15
18
  json_content
16
19
  end
17
- Digest::SHA1.hexdigest(content_for_sha)
20
+ measure_info("Generating SHA1 hexdigest for pact", payload: { length: content_for_sha.length } ){ Digest::SHA1.hexdigest(content_for_sha) }
18
21
  end
19
22
 
20
- def self.extract_verifiable_content_for_sha json_content
21
- Content.from_json(json_content).sort.content_that_affects_verification_results.to_json
23
+ def self.extract_verifiable_content_for_sha(json_content)
24
+ objects = Content.from_json(json_content)
25
+ sorted_content = measure_info("Sorting content", payload: { length: json_content.length }){ objects.sort }
26
+ sorted_content.content_that_affects_verification_results.to_json
22
27
  end
23
28
  end
24
29
  end
@@ -20,7 +20,7 @@ module PactBroker
20
20
  )
21
21
  end
22
22
 
23
- def self.from_request request, path_info
23
+ def self.from_request(request, path_info)
24
24
  json_content = request.body.to_s
25
25
  parsed_content = begin
26
26
  parsed = JSON.parse(json_content, PACT_PARSING_OPTIONS)
@@ -22,6 +22,10 @@ module PactBroker
22
22
  extend PactBroker::Messages
23
23
  extend SquashPactsForVerification
24
24
 
25
+ def generate_sha(json_content)
26
+ PactBroker.configuration.sha_generator.call(json_content)
27
+ end
28
+
25
29
  def find_latest_pact params
26
30
  pact_repository.find_latest_pact(params[:consumer_name], params[:provider_name], params[:tag], params[:branch_name])
27
31
  end
@@ -154,10 +158,12 @@ module PactBroker
154
158
  end
155
159
 
156
160
  # Overwriting an existing pact with the same consumer/provider/consumer version number
161
+ # by creating a new revision (that is, a new PactPublication with an incremented revision number)
162
+ # Modifing pacts is strongly discouraged now, and support for it will be dropped in the next major version of the Pact Broker
157
163
  def create_pact_revision params, existing_pact
158
164
  logger.info("Updating existing pact publication", params.without(:json_content))
159
165
  logger.debug("Content #{params[:json_content]}")
160
- pact_version_sha = generate_sha(params[:json_content])
166
+ pact_version_sha = params.fetch(:pact_version_sha)
161
167
  json_content = add_interaction_ids(params[:json_content])
162
168
  update_params = { pact_version_sha: pact_version_sha, json_content: json_content }
163
169
  updated_pact = pact_repository.update(existing_pact.id, update_params)
@@ -178,21 +184,20 @@ module PactBroker
178
184
 
179
185
  private :create_pact_revision
180
186
 
181
- def disallowed_modification?(existing_pact, new_json_content)
182
- !PactBroker.configuration.allow_dangerous_contract_modification && existing_pact && existing_pact.pact_version_sha != generate_sha(new_json_content)
187
+ def disallowed_modification?(existing_pact, new_pact_version_sha)
188
+ !PactBroker.configuration.allow_dangerous_contract_modification && existing_pact && existing_pact.pact_version_sha != new_pact_version_sha
183
189
  end
184
190
 
185
191
  # When no publication for the given consumer/provider/consumer version number exists
186
- def create_pact params, version, provider
192
+ def create_pact(params, version, provider)
187
193
  logger.info("Creating new pact publication", params.without(:json_content))
188
194
  logger.debug("Content #{params[:json_content]}")
189
- pact_version_sha = generate_sha(params[:json_content])
190
195
  json_content = add_interaction_ids(params[:json_content])
191
196
  pact = pact_repository.create(
192
197
  version_id: version.id,
193
198
  provider_id: provider.id,
194
199
  consumer_id: version.pacticipant_id,
195
- pact_version_sha: pact_version_sha,
200
+ pact_version_sha: params.fetch(:pact_version_sha),
196
201
  json_content: json_content,
197
202
  version: version
198
203
  )
@@ -212,12 +217,6 @@ module PactBroker
212
217
 
213
218
  private :create_pact
214
219
 
215
- def generate_sha(json_content)
216
- PactBroker.configuration.sha_generator.call(json_content)
217
- end
218
-
219
- private :generate_sha
220
-
221
220
  def add_interaction_ids(json_content)
222
221
  Content.from_json(json_content).with_ids.to_json
223
222
  end
@@ -256,7 +256,13 @@ module PactBroker
256
256
  def publish_pact(consumer_name:, provider_name:, consumer_version_number: , tags: nil, branch: nil, build_url: nil, json_content: nil)
257
257
  json_content = json_content || random_json_content(consumer_name, provider_name)
258
258
  contracts = [
259
- PactBroker::Contracts::ContractToPublish.from_hash(consumer_name: consumer_name, provider_name: provider_name, decoded_content: json_content, content_type: "application/json", specification: "pact")
259
+ PactBroker::Contracts::ContractToPublish.from_hash(
260
+ consumer_name: consumer_name,
261
+ provider_name: provider_name,
262
+ decoded_content: json_content,
263
+ content_type: "application/json",
264
+ specification: "pact",
265
+ pact_version_sha: PactBroker::Pacts::GenerateSha.call(json_content))
260
266
  ]
261
267
  contracts_to_publish = PactBroker::Contracts::ContractsToPublish.from_hash(
262
268
  pacticipant_name: consumer_name,
@@ -10,8 +10,10 @@ module PactBroker
10
10
  using PactBroker::StringRefinements
11
11
 
12
12
  set :root, File.join(File.dirname(__FILE__), "..")
13
- set :show_exceptions, ENV["RACK_ENV"] != "production"
14
- set :dump_errors, false # The padrino logger logs these for us. If this is enabled we get duplicate logging.
13
+ set :show_exceptions, ENV["RACK_ENV"] == "development"
14
+ # The padrino logger logs these for us, but only in production. If this is enabled we get duplicate logging.
15
+ set :dump_errors, ENV["RACK_ENV"] != "production"
16
+ set :raise_errors, ENV["RACK_ENV"] == "test"
15
17
 
16
18
  def base_url
17
19
  # Using the X-Forwarded headers in the UI can leave the app vulnerable
@@ -1,14 +1,25 @@
1
- require "pact_broker/configuration"
1
+ # Must be defined before loading Padrino
2
2
  # Stop Padrino creating a log file, as it will try to create it in the gems directory
3
3
  # http://www.padrinorb.com/api/Padrino/Logger.html
4
- unless defined? PADRINO_LOGGER
5
- log_path = File.join(PactBroker.configuration.log_dir, "ui.log")
6
- PADRINO_LOGGER = {
7
- production: { log_level: :error, stream: :to_file, log_path: log_path },
8
- staging: { log_level: :error, stream: :to_file, log_path: log_path },
9
- test: { log_level: :warn, stream: :to_file, log_path: log_path },
10
- development: { log_level: :warn, stream: :to_file, log_path: log_path }
11
- }
4
+ # This configuration will be replaced by the SemanticLogger later on.
5
+ PADRINO_LOGGER ||= {
6
+ ENV.fetch("RACK_ENV", "production").to_sym => { stream: :stdout }
7
+ }
8
+
9
+ require "padrino-core"
10
+
11
+ class PactBrokerPadrinoLogger < SemanticLogger::Logger
12
+ include Padrino::Logger::Extensions
13
+
14
+ # Padrino expects level to return an integer, not a symbol
15
+ def level
16
+ Padrino::Logger::Levels[SemanticLogger.default_level]
17
+ end
12
18
  end
13
19
 
20
+ Padrino.logger = PactBrokerPadrinoLogger.new("Padrino")
21
+ # Log a test message to ensure that the logger works properly, as it only
22
+ # seems to be used in production.
23
+ Padrino.logger.info("Padrino has been configured with SemanticLogger")
24
+
14
25
  require "pact_broker/ui/app"
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = "2.108.0"
2
+ VERSION = "2.109.0"
3
3
  end
data/pact_broker.gemspec CHANGED
@@ -64,7 +64,7 @@ Gem::Specification.new do |gem|
64
64
  gem.add_runtime_dependency "padrino-core", ">= 0.14.3", "~> 0.14"
65
65
  gem.add_runtime_dependency "sinatra", "~> 3.0"
66
66
  gem.add_runtime_dependency "haml", "~>5.0"
67
- gem.add_runtime_dependency "sucker_punch", "~>2.0"
67
+ gem.add_runtime_dependency "sucker_punch", "~>3.0"
68
68
  gem.add_runtime_dependency "rack-protection", "~> 3.0"
69
69
  gem.add_runtime_dependency "table_print", "~> 1.5"
70
70
  gem.add_runtime_dependency "semantic_logger", "~> 4.11"
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.108.0
4
+ version: 2.109.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: 2024-01-05 00:00:00.000000000 Z
13
+ date: 2024-02-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -258,14 +258,14 @@ dependencies:
258
258
  requirements:
259
259
  - - "~>"
260
260
  - !ruby/object:Gem::Version
261
- version: '2.0'
261
+ version: '3.0'
262
262
  type: :runtime
263
263
  prerelease: false
264
264
  version_requirements: !ruby/object:Gem::Requirement
265
265
  requirements:
266
266
  - - "~>"
267
267
  - !ruby/object:Gem::Version
268
- version: '2.0'
268
+ version: '3.0'
269
269
  - !ruby/object:Gem::Dependency
270
270
  name: rack-protection
271
271
  requirement: !ruby/object:Gem::Requirement
@@ -1260,7 +1260,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1260
1260
  - !ruby/object:Gem::Version
1261
1261
  version: '0'
1262
1262
  requirements: []
1263
- rubygems_version: 3.5.4
1263
+ rubygems_version: 3.5.5
1264
1264
  signing_key:
1265
1265
  specification_version: 4
1266
1266
  summary: See description