pact_broker 2.118.0 → 2.119.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/db/migrations/20251106_add_branch_versions_version_id_branch_name_index.rb +21 -0
- data/db/migrations/20260119_add_index_on_consumer_id_provider_id_to_pact_versions.rb +20 -0
- data/db/migrations/20260123_drop_branch_versions_branch_name_index.rb +17 -0
- data/db/migrations/20260210_add_index_on_pact_version_id_to_latest_pact_publication.rb +22 -0
- data/db/migrations/20260217_add_wip_query_optimization_indexes.rb +31 -0
- data/lib/pact_broker/api/decorators/version_decorator.rb +1 -1
- data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -1
- data/lib/pact_broker/configuration.rb +26 -0
- data/lib/pact_broker/deployments/deployed_version_service.rb +1 -0
- data/lib/pact_broker/domain/pacticipant.rb +12 -1
- data/lib/pact_broker/logging.rb +10 -0
- data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
- data/lib/pact_broker/pacts/pact_publication_wip_dataset_module.rb +13 -4
- data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +90 -2
- data/lib/pact_broker/pacts/repository.rb +2 -2
- data/lib/pact_broker/pacts/service.rb +1 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/branch_version_repository.rb +1 -1
- data/lib/pact_broker/webhooks/event_listener.rb +1 -1
- data/lib/pact_broker/webhooks/execution_configuration.rb +4 -0
- data/lib/pact_broker/webhooks/redact_logs.rb +4 -4
- data/lib/sequel/plugins/upsert.rb +7 -0
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e28c13c74efb9e4cd894da6d14e18a28ea6d15c3240f4defc2b2fe246d1fafb0
|
|
4
|
+
data.tar.gz: 6194f39dd67344eed51782de55bea6d95ee937716bd59afbe8b6ee2cfe473b08
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40138782cf26259e00e0f2417d9243f5066f4b6dd77ad076869f12f47adcfb7f4a85481e4b9f3cff4ba9ba9bf03881bbb69e71fa7b269bd868a472ed74c2b91e
|
|
7
|
+
data.tar.gz: 056be7ffcb8273b41d7e1292307b495a6277b71248b685515185f0239b477aa8fb6ff6646ef9ac22e86cfb87aa10bfe969ff93a1868ef10e1e81a3ae793f381a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
<a name="v2.119.0"></a>
|
|
2
|
+
### v2.119.0 (2026-04-02)
|
|
3
|
+
|
|
4
|
+
#### Features
|
|
5
|
+
|
|
6
|
+
* support customising webhook requests ([d20a8274](/../../commit/d20a8274))
|
|
7
|
+
* Optimize WIP Query Performance to Prevent Timeouts on Large Datasets (#902) ([711ac3c6](/../../commit/711ac3c6))
|
|
8
|
+
|
|
9
|
+
#### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* add missing import and small improvement to batch delete (#899) ([2e83d012](/../../commit/2e83d012))
|
|
12
|
+
* new unit test failure ([6c39e927](/../../commit/6c39e927))
|
|
13
|
+
* avoid N+1 cascade ([681d98d9](/../../commit/681d98d9))
|
|
14
|
+
* dont create index for mysql ([1ec72cc8](/../../commit/1ec72cc8))
|
|
15
|
+
* Pacticipant API returning undeployed versions in pacticipant deployed-environments endpoint (#889) ([e217d3ba](/../../commit/e217d3ba))
|
|
16
|
+
|
|
17
|
+
* **PACT-5504**
|
|
18
|
+
* use batch delete to speed up deletion ([e908035a](/../../commit/e908035a))
|
|
19
|
+
|
|
1
20
|
<a name="v2.118.0"></a>
|
|
2
21
|
### v2.118.0 (2026-01-02)
|
|
3
22
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative "migration_helper"
|
|
2
|
+
|
|
3
|
+
include PactBroker::MigrationHelper
|
|
4
|
+
|
|
5
|
+
Sequel.migration do
|
|
6
|
+
up do
|
|
7
|
+
if !mysql?
|
|
8
|
+
alter_table(:branch_versions) do
|
|
9
|
+
add_index([:version_id, :branch_name], name: "branch_versions_version_id_branch_name_idx")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
down do
|
|
15
|
+
if !mysql?
|
|
16
|
+
alter_table(:branch_versions) do
|
|
17
|
+
drop_index([:version_id, :branch_name], name: "branch_versions_version_id_branch_name_idx")
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
up do
|
|
3
|
+
if !mysql?
|
|
4
|
+
alter_table(:pact_versions) do
|
|
5
|
+
add_index :consumer_id, name: :pact_versions_consumer_id_index
|
|
6
|
+
add_index :provider_id, name: :pact_versions_provider_id_index
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
down do
|
|
12
|
+
if !mysql?
|
|
13
|
+
alter_table(:pact_versions) do
|
|
14
|
+
drop_index :consumer_id, name: :pact_versions_consumer_id_index
|
|
15
|
+
drop_index :provider_id, name: :pact_versions_provider_id_index
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Sequel.migration do
|
|
2
|
+
up do
|
|
3
|
+
if !mysql?
|
|
4
|
+
alter_table(:branch_versions) do
|
|
5
|
+
drop_index([:branch_name], name: "branch_versions_branch_name_index")
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
down do
|
|
11
|
+
if !mysql?
|
|
12
|
+
alter_table(:branch_versions) do
|
|
13
|
+
add_index([:branch_name], name: "branch_versions_branch_name_index")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative "migration_helper"
|
|
2
|
+
|
|
3
|
+
include PactBroker::MigrationHelper
|
|
4
|
+
|
|
5
|
+
Sequel.migration do
|
|
6
|
+
up do
|
|
7
|
+
if !mysql?
|
|
8
|
+
alter_table(:latest_pact_publication_ids_for_consumer_versions) do
|
|
9
|
+
add_index :pact_version_id, name: :latest_pp_ids_for_cons_ver_pact_version_id_index
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
down do
|
|
15
|
+
if !mysql?
|
|
16
|
+
alter_table(:latest_pact_publication_ids_for_consumer_versions) do
|
|
17
|
+
drop_index :pact_version_id, name: :latest_pp_ids_for_cons_ver_pact_version_id_index
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative "migration_helper"
|
|
2
|
+
|
|
3
|
+
include PactBroker::MigrationHelper
|
|
4
|
+
|
|
5
|
+
Sequel.migration do
|
|
6
|
+
no_transaction if PactBroker::MigrationHelper.postgres?
|
|
7
|
+
|
|
8
|
+
up do
|
|
9
|
+
if !mysql?
|
|
10
|
+
alter_table(:pact_publications) do
|
|
11
|
+
add_index([:provider_id, :created_at], name: "idx_pp_provider_created", concurrently: postgres?)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
alter_table(:verifications) do
|
|
15
|
+
add_index([:provider_id, :provider_version_id, :success, :wip, :pact_version_id], name: "idx_verifications_provider_lookup", concurrently: postgres?)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
down do
|
|
21
|
+
if !mysql?
|
|
22
|
+
alter_table(:pact_publications) do
|
|
23
|
+
drop_index([:provider_id, :created_at], name: "idx_pp_provider_created")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
alter_table(:verifications) do
|
|
27
|
+
drop_index([:provider_id, :provider_version_id, :success, :wip, :pact_version_id], name: "idx_verifications_provider_lookup")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -25,7 +25,7 @@ module PactBroker
|
|
|
25
25
|
def self.eager_load_associations
|
|
26
26
|
[
|
|
27
27
|
:pacticipant,
|
|
28
|
-
:
|
|
28
|
+
{ pact_publications: [:consumer, :provider, { pact_version: :latest_verification }, :tags, :head_pact_publications_for_tags] },
|
|
29
29
|
{ branch_versions: [:version, :branch_head, { branch: :pacticipant }] },
|
|
30
30
|
{ tags: :head_tag }
|
|
31
31
|
]
|
|
@@ -94,7 +94,7 @@ module PactBroker
|
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def log_request
|
|
97
|
-
logger.
|
|
97
|
+
logger.debug "Fetching pacts for verification by #{provider_name}", provider_name: provider_name, params: query
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def nested_query
|
|
@@ -201,6 +201,32 @@ module PactBroker
|
|
|
201
201
|
webhook_host_whitelist&.any?
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
+
def dynamic_wip_window_enabled?
|
|
205
|
+
ENV["PACT_BROKER_DYNAMIC_WIP_WINDOW"]&.downcase == "true"
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Maximum lookback window (days) to query for unverified pacts. Prevents timeout on large datasets.
|
|
209
|
+
def max_wip_lookback_days
|
|
210
|
+
ENV["PACT_BROKER_MAX_WIP_LOOKBACK_DAYS"]&.to_i || 14
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Minimum WIP window (days) to ensure recent pacts are always included, even if P80 is very low.
|
|
214
|
+
def min_wip_window_days
|
|
215
|
+
ENV["PACT_BROKER_MIN_WIP_WINDOW_DAYS"]&.to_i || 7
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Lookback window (days) for checking if a pact was verified by another branch before this branch was created.
|
|
219
|
+
# Uses verification execution_date to find recent branch activity, avoiding false negatives when old
|
|
220
|
+
# version numbers are reused across branches.
|
|
221
|
+
def verified_by_other_branch_before_this_branch_look_back
|
|
222
|
+
ENV["PACT_BROKER_VERIFIED_BY_OTHER_BRANCH_LOOKBACK_DAYS"]&.to_i || 30
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Default WIP window (days) used when dynamic calculation fails or no unverified pacts exist.
|
|
226
|
+
def default_wip_window_days
|
|
227
|
+
ENV["PACT_BROKER_DEFAULT_WIP_WINDOW_DAYS"]&.to_i || 7
|
|
228
|
+
end
|
|
229
|
+
|
|
204
230
|
def enable_badge_resources= enable_badge_resources
|
|
205
231
|
puts "Pact Broker configuration property `enable_badge_resources` is deprecated. Please use `enable_public_badge_access`"
|
|
206
232
|
self.enable_public_badge_access = enable_badge_resources
|
|
@@ -55,11 +55,22 @@ module PactBroker
|
|
|
55
55
|
PactBroker::Pacts::PactPublication.where(provider: self).delete
|
|
56
56
|
PactBroker::Domain::Verification.where(consumer: self).or(provider: self).delete
|
|
57
57
|
PactBroker::Domain::Version.where(pacticipant: self).delete
|
|
58
|
-
|
|
58
|
+
delete_pact_versions_in_batches
|
|
59
59
|
PactBroker::Domain::Label.where(pacticipant: self).destroy
|
|
60
60
|
super
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
BATCH_DELETE_SIZE = 500
|
|
64
|
+
def delete_pact_versions_in_batches
|
|
65
|
+
dataset = PactBroker::Pacts::PactVersion.where(consumer: self).or(provider: self).order(:id)
|
|
66
|
+
loop do
|
|
67
|
+
deleted = PactBroker::Pacts::PactVersion
|
|
68
|
+
.where(id: dataset.limit(BATCH_DELETE_SIZE).select(:id))
|
|
69
|
+
.delete
|
|
70
|
+
break if deleted.zero?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
63
74
|
def before_save
|
|
64
75
|
super
|
|
65
76
|
self.display_name = generate_display_name(name) if display_name.blank?
|
data/lib/pact_broker/logging.rb
CHANGED
|
@@ -52,6 +52,16 @@ module PactBroker
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def measure_debug(message, payload: {})
|
|
56
|
+
if logger.respond_to?(:measure_debug)
|
|
57
|
+
logger.measure_debug(message, payload: payload) do
|
|
58
|
+
yield
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
yield
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
55
65
|
def log_error e, description = nil
|
|
56
66
|
if logger.instance_of?(SemanticLogger::Logger)
|
|
57
67
|
if description
|
|
@@ -287,9 +287,13 @@ module PactBroker
|
|
|
287
287
|
# NEW LOGIC
|
|
288
288
|
# @return [Sequel::Dataset<PactBroker::Pacts::PactPublication>]
|
|
289
289
|
def for_all_tag_heads
|
|
290
|
+
# Optimization: Only aggregate tags for consumers that have pacts for this provider.
|
|
291
|
+
relevant_consumer_ids = self.select(:consumer_id).distinct
|
|
292
|
+
|
|
290
293
|
head_tags = PactBroker::Domain::Tag
|
|
291
294
|
.select_group(:pacticipant_id, :name)
|
|
292
|
-
.select_append{ max(version_order).as(:latest_version_order) }
|
|
295
|
+
.select_append{ max(:version_order).as(:latest_version_order) }
|
|
296
|
+
.where(pacticipant_id: relevant_consumer_ids)
|
|
293
297
|
|
|
294
298
|
head_tags_join = {
|
|
295
299
|
Sequel[:pact_publications][:consumer_id] => Sequel[:head_tags][:pacticipant_id],
|
|
@@ -19,13 +19,19 @@ module PactBroker
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def join_branch_versions_excluding_branch(provider_id, branch_name)
|
|
22
|
+
# Only check branches that have been active in the last N days
|
|
23
|
+
# to avoid old stale branches causing cartesian explosion.
|
|
24
|
+
# Filter by verification execution_date rather than version created_at
|
|
25
|
+
# to avoid incorrectly excluding old versions with recent verifications.
|
|
26
|
+
recent_branch_cutoff = DateTime.now - PactBroker.configuration.verified_by_other_branch_before_this_branch_look_back
|
|
27
|
+
|
|
22
28
|
branch_versions_join = {
|
|
23
29
|
Sequel[:verifications][:provider_version_id] => Sequel[:branch_versions][:version_id],
|
|
24
30
|
Sequel[:branch_versions][:pacticipant_id] => provider_id
|
|
25
31
|
}
|
|
26
|
-
join(:branch_versions, branch_versions_join)
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
join(:branch_versions, branch_versions_join)
|
|
33
|
+
.where { Sequel[:verifications][:execution_date] >= recent_branch_cutoff }
|
|
34
|
+
.where { Sequel.lit("branch_versions.branch_name != ?", branch_name) }
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
def join_provider_versions_for_provider_id_and_branch(provider_id, provider_version_branch)
|
|
@@ -45,10 +51,13 @@ module PactBroker
|
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
def successfully_verified_by_provider_branch_when_not_wip(provider_id, provider_version_branch)
|
|
54
|
+
# Only check verifications from last N days (old verifications unlikely relevant for WIP)
|
|
55
|
+
recent_cutoff = DateTime.now - PactBroker.configuration.verified_by_other_branch_before_this_branch_look_back
|
|
48
56
|
successful_verifications = VerificationForWipCalculations
|
|
49
57
|
.select(:pact_version_id)
|
|
50
58
|
.distinct
|
|
51
59
|
.successful_non_wip_by_provider(provider_id)
|
|
60
|
+
.where { Sequel[:verifications][:execution_date] >= recent_cutoff }
|
|
52
61
|
.join_provider_versions_for_provider_id_and_branch(provider_id, provider_version_branch)
|
|
53
62
|
|
|
54
63
|
|
|
@@ -65,8 +74,8 @@ module PactBroker
|
|
|
65
74
|
.select(:pact_version_id)
|
|
66
75
|
.distinct
|
|
67
76
|
.successful_non_wip_by_provider(provider_id)
|
|
68
|
-
.join_branch_versions_excluding_branch(provider_id, provider_version_branch)
|
|
69
77
|
.verified_before_creation_date_of(first_version_for_branch)
|
|
78
|
+
.join_branch_versions_excluding_branch(provider_id, provider_version_branch)
|
|
70
79
|
|
|
71
80
|
from_self(alias: :pp)
|
|
72
81
|
.select(Sequel[:pp].*)
|
|
@@ -57,7 +57,7 @@ module PactBroker
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
provider = pacticipant_repository.find_by_name(provider_name)
|
|
60
|
-
wip_start_date = options
|
|
60
|
+
wip_start_date = determine_wip_start_date(provider, options)
|
|
61
61
|
|
|
62
62
|
wip_by_consumer_tags = find_wip_pact_versions_for_provider_by_provider_tags(
|
|
63
63
|
provider,
|
|
@@ -80,6 +80,94 @@ module PactBroker
|
|
|
80
80
|
|
|
81
81
|
private
|
|
82
82
|
|
|
83
|
+
# Determine the WIP start date based on configuration and options
|
|
84
|
+
def determine_wip_start_date(provider, options)
|
|
85
|
+
dynamic_enabled = PactBroker.configuration.dynamic_wip_window_enabled?
|
|
86
|
+
logger.debug("Dynamic WIP window: #{dynamic_enabled ? 'enabled' : 'disabled'}") if logger.debug?
|
|
87
|
+
|
|
88
|
+
if dynamic_enabled
|
|
89
|
+
calculate_wip_window(provider)
|
|
90
|
+
else
|
|
91
|
+
wip_start_date = options.fetch(:include_wip_pacts_since)
|
|
92
|
+
logger.debug("Using user-specified WIP window: #{wip_start_date}") if logger.debug?
|
|
93
|
+
wip_start_date
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Calculate optimal WIP window using P80 (80th percentile) of unverified pact ages
|
|
98
|
+
# Uses percentile instead of MAX to focus on active work and ignore abandoned pacts
|
|
99
|
+
# Returns: Date from N days ago (min 7 for weekly coverage, max 14 to align with query scope)
|
|
100
|
+
def calculate_wip_window(provider)
|
|
101
|
+
# Step 1: Get list of unverified pact ages in days (e.g., [2, 5, 7, 10, 12, 14])
|
|
102
|
+
unverified_ages = get_unverified_pact_ages_in_days(provider)
|
|
103
|
+
|
|
104
|
+
# Step 2: If no unverified pacts, use default configured window
|
|
105
|
+
if unverified_ages.empty?
|
|
106
|
+
default_window = PactBroker.configuration.default_wip_window_days
|
|
107
|
+
logger.debug("No unverified pacts found for #{provider.name}, using default #{default_window}-day window") if logger.debug?
|
|
108
|
+
return Date.today - default_window
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Step 3: Calculate 80th percentile (ignore oldest 20% as abandoned work)
|
|
112
|
+
p80_age = percentile_80(unverified_ages)
|
|
113
|
+
|
|
114
|
+
# Step 4: Ensure window is between configured min-max days, then return date
|
|
115
|
+
min_window = PactBroker.configuration.min_wip_window_days
|
|
116
|
+
max_window = PactBroker.configuration.max_wip_lookback_days
|
|
117
|
+
window_days = clamp_between(p80_age, min_window, max_window)
|
|
118
|
+
wip_start_date = Date.today - window_days
|
|
119
|
+
|
|
120
|
+
if logger.debug?
|
|
121
|
+
logger.debug("Dynamic WIP window for #{provider.name}: found #{unverified_ages.size} unverified pacts, P80=#{p80_age} days, window=#{window_days} days (#{wip_start_date})")
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
wip_start_date
|
|
125
|
+
rescue StandardError => e
|
|
126
|
+
default_window = PactBroker.configuration.default_wip_window_days
|
|
127
|
+
logger.error("Failed to calculate dynamic WIP window for provider #{provider.name}", error: e.class.name, message: e.message)
|
|
128
|
+
Date.today - default_window
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Get ages (in days) of pacts that haven't been successfully verified yet
|
|
132
|
+
# Only looks back the configured max_wip_lookback_days to keep query fast
|
|
133
|
+
def get_unverified_pact_ages_in_days(provider)
|
|
134
|
+
max_days = PactBroker.configuration.max_wip_lookback_days
|
|
135
|
+
cutoff_date = DateTime.now - max_days
|
|
136
|
+
|
|
137
|
+
# Use database-specific date arithmetic that works on both PostgreSQL and SQLite
|
|
138
|
+
# PostgreSQL: CURRENT_DATE - created_at::date returns integer days
|
|
139
|
+
# SQLite: julianday(date('now')) - julianday(date(created_at)) returns float days
|
|
140
|
+
db = PactPublication.db
|
|
141
|
+
age_expr = if db.database_type == :postgres
|
|
142
|
+
Sequel.lit("CURRENT_DATE - ?::date", Sequel[:pact_publications][:created_at])
|
|
143
|
+
else
|
|
144
|
+
# SQLite
|
|
145
|
+
Sequel.function(:julianday, Sequel.function(:date, "now")) -
|
|
146
|
+
Sequel.function(:julianday, Sequel.function(:date, Sequel[:pact_publications][:created_at]))
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
PactPublication
|
|
150
|
+
.where(Sequel[:pact_publications][:provider_id] => provider.id)
|
|
151
|
+
.where { Sequel[:pact_publications][:created_at] >= cutoff_date }
|
|
152
|
+
.left_join(:verifications, { pact_version_id: :pact_version_id, success: true })
|
|
153
|
+
.where(Sequel[:verifications][:id] => nil)
|
|
154
|
+
.select_map { Sequel.cast(age_expr, :integer).as(:age_days) }
|
|
155
|
+
.compact
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Calculate 80th percentile: find value where 80% of data points are less than or equal
|
|
159
|
+
# Example: [2,3,5,7,10,12,14,20] → 80% index = 6 → value = 14
|
|
160
|
+
def percentile_80(values)
|
|
161
|
+
sorted = values.sort
|
|
162
|
+
index = [(sorted.length * 0.80).ceil - 1, 0].max
|
|
163
|
+
sorted[index]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Constrain value between min and max bounds
|
|
167
|
+
def clamp_between(value, min, max)
|
|
168
|
+
[[value, max].min, min].max
|
|
169
|
+
end
|
|
170
|
+
|
|
83
171
|
# Note: created_at is coming back as a string for sqlite
|
|
84
172
|
# Can't work out how to to tell Sequel that this should be a date
|
|
85
173
|
def to_datetime string_or_datetime
|
|
@@ -227,7 +315,7 @@ module PactBroker
|
|
|
227
315
|
|
|
228
316
|
def find_wip_pact_versions_for_provider_by_provider_branch(provider_name, provider_version_branch, explicitly_specified_verifiable_pacts, options)
|
|
229
317
|
provider = pacticipant_repository.find_by_name(provider_name)
|
|
230
|
-
wip_start_date = options
|
|
318
|
+
wip_start_date = determine_wip_start_date(provider, options)
|
|
231
319
|
|
|
232
320
|
potential_wip_by_consumer_branch = PactPublication.for_provider(provider).created_after(wip_start_date).for_all_branch_heads
|
|
233
321
|
potential_wip_by_consumer_tag = PactPublication.for_provider(provider).created_after(wip_start_date).for_all_tag_heads
|
|
@@ -149,7 +149,7 @@ module PactBroker
|
|
|
149
149
|
query = query.overall_latest
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
-
query.all.
|
|
152
|
+
query.all.sort.collect(&:to_head_pact)
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
def find_pacts_by_consumer_branch(provider_name, options = {})
|
|
@@ -179,7 +179,7 @@ module PactBroker
|
|
|
179
179
|
query = query.for_branch_name(branch)
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
|
-
query.all.
|
|
182
|
+
query.all.sort.collect(&:to_head_pact)
|
|
183
183
|
end
|
|
184
184
|
|
|
185
185
|
def find_for_verification(provider_name, consumer_version_selectors)
|
|
@@ -210,7 +210,7 @@ module PactBroker
|
|
|
210
210
|
|
|
211
211
|
# When no publication for the given consumer/provider/consumer version number exists
|
|
212
212
|
def create_pact(params, version, provider)
|
|
213
|
-
logger.
|
|
213
|
+
logger.debug("Creating new pact publication", params.without(:json_content))
|
|
214
214
|
logger.debug("Content #{params[:json_content]}")
|
|
215
215
|
json_content = add_interaction_ids(params[:json_content])
|
|
216
216
|
pact = pact_repository.create(
|
data/lib/pact_broker/version.rb
CHANGED
|
@@ -22,7 +22,7 @@ module PactBroker
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def add_branch(version, branch_name, auto_created: false)
|
|
25
|
-
PactBroker.logger.
|
|
25
|
+
PactBroker.logger.debug("BranchVersionRepository#add_branch method called with version #{version.inspect} and branch_name '#{branch_name}'")
|
|
26
26
|
Sequel::Model.db.transaction do
|
|
27
27
|
branch = find_or_create_branch(version.pacticipant, branch_name)
|
|
28
28
|
branch_version = version.branch_version_for_branch(branch)
|
|
@@ -56,7 +56,7 @@ module PactBroker
|
|
|
56
56
|
logger.debug("Event detected", event_name: event.name, event_comment: event.comment)
|
|
57
57
|
if event.triggered_webhooks&.any?
|
|
58
58
|
triggered_webhook_descriptions = event.triggered_webhooks.collect{ |tw| { event_name: event.name, webhook_uuid: tw.webhook_uuid, triggered_webhook_uuid: tw.uuid, webhook_description: tw.webhook.description } }
|
|
59
|
-
logger.
|
|
59
|
+
logger.debug("Triggered webhooks for #{event.name}", triggered_webhooks: triggered_webhook_descriptions)
|
|
60
60
|
else
|
|
61
61
|
logger.debug "No enabled webhooks found for event #{event.name}"
|
|
62
62
|
end
|
|
@@ -7,12 +7,12 @@ module PactBroker
|
|
|
7
7
|
|
|
8
8
|
using PactBroker::StringRefinements
|
|
9
9
|
|
|
10
|
-
def redact_logs(logs, values)
|
|
11
|
-
RedactLogs.call(logs, values)
|
|
10
|
+
def redact_logs(logs, values, pattern_substitutions = [])
|
|
11
|
+
RedactLogs.call(logs, values, pattern_substitutions)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def self.call logs, values
|
|
15
|
-
substitutions = HEADER_SUBSTITUTIONS + value_substitutions(values)
|
|
14
|
+
def self.call logs, values, pattern_substitutions = []
|
|
15
|
+
substitutions = HEADER_SUBSTITUTIONS + pattern_substitutions + value_substitutions(values)
|
|
16
16
|
|
|
17
17
|
substitutions.reduce(logs) do | agg_logs, (find, replace) |
|
|
18
18
|
agg_logs.gsub(find, replace)
|
|
@@ -68,6 +68,7 @@ module Sequel
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def manual_upsert(opts)
|
|
71
|
+
sync_deserialized_values_to_values
|
|
71
72
|
# Can use slice when we drop support for Ruby 2.4
|
|
72
73
|
query = values.select{ |k, _| self.class.upsert_plugin_identifying_columns.include?(k) }
|
|
73
74
|
existing_record = model.where(query).single_record
|
|
@@ -79,6 +80,12 @@ module Sequel
|
|
|
79
80
|
end
|
|
80
81
|
end
|
|
81
82
|
|
|
83
|
+
def sync_deserialized_values_to_values
|
|
84
|
+
if respond_to?(:deserialized_values) && deserialized_values.is_a?(Hash)
|
|
85
|
+
deserialized_values.each { |k, v| values[k] = v }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
82
89
|
# naughty override of Sequel private method to
|
|
83
90
|
# avoid having to rewrite the whole save method logic
|
|
84
91
|
def _insert_dataset
|
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.
|
|
4
|
+
version: 2.119.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bethany Skurrie
|
|
@@ -579,8 +579,13 @@ files:
|
|
|
579
579
|
- db/migrations/20231002_add_version_id_index_to_released_version.rb
|
|
580
580
|
- db/migrations/20231003_add_version_id_index_to_deployed_version.rb
|
|
581
581
|
- db/migrations/20240112_add_client_language_verified_by_to_verification.rb
|
|
582
|
+
- db/migrations/20251106_add_branch_versions_version_id_branch_name_index.rb
|
|
582
583
|
- db/migrations/20251202_add_has_messages_column_to_pact_versions.rb
|
|
583
584
|
- db/migrations/20251202_populate_has_messages_in_pact_versions.rb
|
|
585
|
+
- db/migrations/20260119_add_index_on_consumer_id_provider_id_to_pact_versions.rb
|
|
586
|
+
- db/migrations/20260123_drop_branch_versions_branch_name_index.rb
|
|
587
|
+
- db/migrations/20260210_add_index_on_pact_version_id_to_latest_pact_publication.rb
|
|
588
|
+
- db/migrations/20260217_add_wip_query_optimization_indexes.rb
|
|
584
589
|
- db/migrations/migration_helper.rb
|
|
585
590
|
- docs/CONFIGURATION.md
|
|
586
591
|
- docs/api/PACTICIPANTS.md
|
|
@@ -1290,7 +1295,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
1290
1295
|
- !ruby/object:Gem::Version
|
|
1291
1296
|
version: '0'
|
|
1292
1297
|
requirements: []
|
|
1293
|
-
rubygems_version: 4.0.
|
|
1298
|
+
rubygems_version: 4.0.10
|
|
1294
1299
|
specification_version: 4
|
|
1295
1300
|
summary: See description
|
|
1296
1301
|
test_files: []
|