Skip to content

Commit f625c9a

Browse files
committed
fix: sort matrix rows by last action date
When there is a bi-directional pact, if there are more than 100 rows in one direction, the rows for the other pact weren't being shown on the matrix page.
1 parent 1e6ef2a commit f625c9a

13 files changed

+158
-51
lines changed

lib/pact_broker/matrix/every_row.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class EveryRow < PactBroker::Matrix::QuickRow
1616
Sequel[:v][:id].as(:verification_id)
1717
]
1818

19-
ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PACT_COLUMNS +
19+
ALL_COLUMNS = [LAST_ACTION_DATE] + CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PACT_COLUMNS +
2020
PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS + VERIFICATION_COLUMNS
2121

2222
SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS

lib/pact_broker/matrix/quick_row.rb

+12-1
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ class QuickRow < Sequel::Model(Sequel.as(:latest_pact_publication_ids_for_consum
5959
VERIFICATION_COLUMNS = [
6060
Sequel[:v][:verification_id]
6161
]
62-
ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PACT_COLUMNS +
62+
LAST_ACTION_DATE = Sequel.lit("CASE WHEN (pv.created_at IS NOT NULL AND pv.created_at > cv.created_at) THEN pv.created_at ELSE cv.created_at END").as(:last_action_date)
63+
64+
ALL_COLUMNS = [LAST_ACTION_DATE] + CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PACT_COLUMNS +
6365
PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS + VERIFICATION_COLUMNS
6466

6567

68+
6669
# cachable select arguments
6770
SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS
6871
SELECT_PACTICIPANT_IDS_ARGS = [:select_pacticipant_ids, Sequel[:p][:consumer_id], Sequel[:p][:provider_id]]
@@ -127,6 +130,10 @@ def order_by_names_ascending_most_recent_first
127130
Sequel.desc(:verification_id))
128131
end
129132

133+
def order_by_last_action_date
134+
order(Sequel.desc(1), Sequel.desc(:consumer_version_order))
135+
end
136+
130137
def eager_all_the_things
131138
eager(:consumer)
132139
.eager(:provider)
@@ -376,6 +383,10 @@ def provider_version_order
376383
return_or_raise_if_not_set(:provider_version_order)
377384
end
378385

386+
def last_action_date
387+
return_or_raise_if_not_set(:last_action_date)
388+
end
389+
379390
def has_verification?
380391
!!verification_id
381392
end

lib/pact_broker/matrix/repository.rb

+12-2
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,25 @@ def apply_latestby options, lines
119119
# that don't have verifications, so we need to include them all.
120120
lines.group_by{|line| group_by_columns.collect{|key| line.send(key) }}
121121
.values
122-
.collect{ | lines | lines.first.provider_version_number.nil? ? lines : lines.first }
122+
.collect{ | lines | lines.first.provider_version_number.nil? ? lines : lines.sort_by(&:provider_version_order).last }
123123
.flatten
124124
end
125125

126126
def query_matrix selectors, options
127127
query = base_model(options)
128128
.select_all_columns
129129
.matching_selectors(selectors)
130-
.order_by_names_ascending_most_recent_first
130+
131+
# These two could both probably use the order by last action date,
132+
# but I just want to give the implications a little more thought before changing
133+
# the can-i-deploy query order.
134+
if selectors.all?(&:only_pacticipant_name_specified?)
135+
# Can only be the UI, as can-i-deploy requires a version to be specified
136+
query = query.order_by_last_action_date
137+
else
138+
query = query.order_by_names_ascending_most_recent_first
139+
end
140+
131141
query = query.limit(options[:limit]) if options[:limit]
132142
query.eager_all_the_things.all
133143
end

lib/pact_broker/matrix/resolved_selector.rb

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ def most_specific_criterion
7979
end
8080
end
8181

82+
def only_pacticipant_name_specified?
83+
pacticipant_name && !tag && !latest?
84+
end
85+
8286
def latest_tagged?
8387
latest? && tag
8488
end

lib/pact_broker/test/test_data_builder.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ def create_pact params = {}
211211
pact_version_sha: pact_version_sha,
212212
json_content: prepare_json_content(json_content),
213213
)
214-
set_created_at_if_set params[:created_at], :pact_publications, {id: @pact.id}
215-
set_created_at_if_set params[:created_at], :pact_versions, {sha: @pact.pact_version_sha}
214+
set_created_at_if_set(params[:created_at], :pact_publications, id: @pact.id)
215+
set_created_at_if_set(params[:created_at], :pact_versions, sha: @pact.pact_version_sha)
216216
@pact = PactBroker::Pacts::PactPublication.find(id: @pact.id).to_domain
217217
self
218218
end
@@ -320,9 +320,13 @@ def create_verification parameters = {}
320320
@verification = PactBroker::Verifications::Repository.new.create(verification, provider_version_number, @pact)
321321
@provider_version = PactBroker::Domain::Version.where(pacticipant_id: @provider.id, number: provider_version_number).single_record
322322

323+
set_created_at_if_set(parameters[:created_at], :verifications, id: @verification.id)
324+
set_created_at_if_set(parameters[:created_at], :versions, id: @provider_version.id)
325+
323326
if tag_names.any?
324327
tag_names.each do | tag_name |
325328
PactBroker::Domain::Tag.create(name: tag_name, version: @provider_version)
329+
set_created_at_if_set(parameters[:created_at], :tags, version_id: @provider_version.id, name: tag_name)
326330
end
327331
end
328332
self

lib/pact_broker/ui/view_models/matrix_line.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,11 @@ def other_provider_version_tags
113113
end
114114

115115
def orderable_fields
116-
[consumer_name, consumer_version_order, pact_revision_number, provider_name, @line.verification_id]
116+
[@line.last_action_date, @line.pact_created_at]
117117
end
118118

119119
def <=> other
120-
(self.orderable_fields <=> other.orderable_fields) * -1
120+
(orderable_fields <=> other.orderable_fields) * -1
121121
end
122122

123123
def verification_status

script/seed.rb

+15-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
ENV['RACK_ENV'] = 'development'
99
require 'sequel'
1010
require 'logger'
11-
DATABASE_CREDENTIALS = {logger: Logger.new($stdout), adapter: "sqlite", database: ARGV[0], :encoding => 'utf8'}.tap { |it| puts it }
11+
logger = Logger.new($stdout)
12+
logger = Logger.new(StringIO.new)
13+
DATABASE_CREDENTIALS = {logger: logger, adapter: "sqlite", database: ARGV[0], :encoding => 'utf8'}.tap { |it| puts it }
1214
#DATABASE_CREDENTIALS = {adapter: "postgres", database: "pact_broker", username: 'pact_broker', password: 'pact_broker', :encoding => 'utf8'}
1315

1416
connection = Sequel.connect(DATABASE_CREDENTIALS)
@@ -42,6 +44,8 @@
4244
'githubVerificationStatus' => '${pactbroker.githubVerificationStatus}'
4345
}
4446

47+
PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results = false
48+
4549
# .create_global_webhook(
4650
# method: 'POST',
4751
# url: "http://localhost:9292/pact-changed-webhook",
@@ -53,6 +57,16 @@
5357
method: 'POST',
5458
url: "http://localhost:9292/verification-published-webhook",
5559
body: webhook_body.to_json)
60+
.set_now(Date.today - 101)
61+
.tap{ | it |
62+
100.times do | i |
63+
it.create_pact_with_verification("Foo", i.to_s, "Bar", i.to_s)
64+
.create_pact_with_verification("Bar", i.to_s, "Foo", i.to_s)
65+
.add_day
66+
end
67+
}.create_pact_with_hierarchy("Foo", "100", "Bar")
68+
69+
5670
# .create_certificate(path: 'spec/fixtures/certificates/self-signed.badssl.com.pem')
5771
# .create_consumer("Bethtest")
5872
# .create_verification_webhook(method: 'GET', url: "http://localhost:9292", body: webhook_body, username: "foo", password: "bar", headers: {"Accept" => "application/json"})

spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb

+1-10
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,7 @@ module Decorators
3636
let(:base_url) { 'http://example.org' }
3737
let(:options) { { user_options: { base_url: base_url } } }
3838
let(:dashboard_json) { DashboardDecorator.new([index_item]).to_json(options) }
39-
let(:created_at) { in_utc { DateTime.new(2018) } }
40-
41-
def in_utc
42-
begin
43-
ENV['TZ'] = 'UTC'
44-
yield
45-
ensure
46-
ENV['TZ'] = ORIGINAL_TZ
47-
end
48-
end
39+
let(:created_at) { td.in_utc { DateTime.new(2018) } }
4940

5041
before do
5142
allow_any_instance_of(DashboardDecorator).to receive(:pact_url).with(base_url, pact).and_return('pact_url')

spec/lib/pact_broker/integrations/integration_spec.rb

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ module Integrations
3838
describe "latest_pact_or_verification_publication_date" do
3939
context "when the last publication is a verification" do
4040
it "returns the verification execution date" do
41-
date = td.in_utc { DateTime.new(2019, 1, 4) }
42-
expect(Integration.first.latest_pact_or_verification_publication_date.to_datetime).to eq date
41+
expect(Integration.first.latest_pact_or_verification_publication_date.to_datetime).to eq Integration.first.latest_verification_publication_date
4342
end
4443
end
4544

spec/lib/pact_broker/matrix/every_row_spec.rb

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ module Matrix
88
let(:bar) { PactBroker::Domain::Pacticipant.where(name: "Bar").single_record }
99
let(:wiffle) { PactBroker::Domain::Pacticipant.where(name: "Wiffle").single_record }
1010

11+
describe "ALL_COLUMNS" do
12+
it "has the same first column as QuickRow - this is required so that they both sort the same" do
13+
expect(EveryRow::ALL_COLUMNS.first).to eq QuickRow::LAST_ACTION_DATE
14+
expect(EveryRow::ALL_COLUMNS.first).to eq QuickRow::ALL_COLUMNS.first
15+
end
16+
end
17+
1118
describe "matching_selectors" do
1219
before do
1320
td.create_pact_with_verification("Foo", "1", "Bar", "2")

spec/lib/pact_broker/matrix/quick_row_spec.rb

+62-20
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,70 @@
44
module PactBroker
55
module Matrix
66
describe QuickRow do
7-
before do
8-
td.create_pact_with_hierarchy("A", "1", "B")
9-
.create_verification(provider_version: '1', success: false)
10-
.create_verification(provider_version: '1', number: 2, success: true)
11-
.create_verification(provider_version: '2', number: 3, success: true)
12-
.create_provider("C")
13-
.create_pact
14-
.create_verification(provider_version: '1')
15-
.create_consumer_version("2")
16-
.create_pact
17-
.create_verification(provider_version: '3')
18-
.use_provider("B")
19-
.create_pact
7+
describe "the interface" do
8+
before do
9+
td.create_pact_with_hierarchy("A", "1", "B")
10+
.create_verification(provider_version: '1', success: false)
11+
.create_verification(provider_version: '1', number: 2, success: true)
12+
.create_verification(provider_version: '2', number: 3, success: true)
13+
.create_provider("C")
14+
.create_pact
15+
.create_verification(provider_version: '1')
16+
.create_consumer_version("2")
17+
.create_pact
18+
.create_verification(provider_version: '3')
19+
.use_provider("B")
20+
.create_pact
21+
end
22+
23+
it "behaves like a Row, except quicker" do
24+
a_id = QuickRow.db[:pacticipants].where(name: "A").select(:id).single_record[:id]
25+
rows = QuickRow.default_scope.where(consumer_id: a_id).eager(:consumer).eager(:verification).all
26+
expect(rows.first.consumer).to be rows.last.consumer
27+
expect(rows.first.verification).to_not be nil
28+
expect(rows.first.consumer_name).to_not be nil
29+
expect(rows.first.provider_name).to_not be nil
30+
end
31+
2032
end
2133

22-
it "behaves like a Row, except quicker" do
23-
a_id = QuickRow.db[:pacticipants].where(name: "A").select(:id).single_record[:id]
24-
rows = QuickRow.default_scope.where(consumer_id: a_id).eager(:consumer).eager(:verification).all
25-
expect(rows.first.consumer).to be rows.last.consumer
26-
expect(rows.first.verification).to_not be nil
27-
expect(rows.first.consumer_name).to_not be nil
28-
expect(rows.first.provider_name).to_not be nil
34+
describe "order_by_last_action_date" do
35+
subject { QuickRow.default_scope.order_by_last_action_date }
36+
37+
context "when there are two pacts verified at the same time" do
38+
before do
39+
td.create_consumer("Foo")
40+
.create_provider("Bar")
41+
.create_consumer_version("10", created_at: cv_created_at_1)
42+
.create_pact
43+
.create_verification(provider_version: "2", created_at: pv_created_at)
44+
.create_consumer_version("3", created_at: cv_created_at_2)
45+
.create_pact
46+
.create_verification(provider_version: "2")
47+
end
48+
49+
let(:cv_created_at_1) { DateTime.now + 1 }
50+
let(:cv_created_at_2) { DateTime.now + 2 }
51+
let(:pv_created_at) { DateTime.now + 3 }
52+
53+
54+
it "orders by the consumer version" do
55+
expect(subject.first.consumer_version_number).to eq "3"
56+
expect(subject.last.consumer_version_number).to eq "10"
57+
end
58+
end
59+
60+
context "when a pact has been published after a pact has been verified" do
61+
before do
62+
td.create_pact_with_verification("Foo", "1", "Bar", "2")
63+
.create_pact_with_hierarchy("Foo", "2", "Bar")
64+
end
65+
66+
it "puts the unverified pact before the verification" do
67+
expect(subject.first.consumer_version_number).to eq "2"
68+
expect(subject.last.consumer_version_number).to eq "1"
69+
end
70+
end
2971
end
3072
end
3173
end

spec/lib/pact_broker/matrix/repository_query_limit_spec.rb

+35-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ module Matrix
1515
# for the entire table, or whether it will apply the consumer/provider filters...
1616
# The quick and dirty solution is to do a pre-query to get the latest pact revision and latest
1717
# verifications for the pact versions before we do the matrix query.
18-
describe "Querying for can-i-deploy when there are more matrix rows than the specified query limit" do
18+
describe "querying for can-i-deploy when there are more matrix rows than the specified query limit" do
1919
before do
2020
td.create_consumer("Foo")
2121
.create_provider("Bar")
@@ -39,6 +39,40 @@ module Matrix
3939
expect(JSON.parse(subject.body)['summary']['deployable']).to be true
4040
end
4141
end
42+
43+
describe "querying for the UI when there is a bi-directional pact and there are more matrix rows for one direction than the specified query limit" do
44+
before do
45+
td.create_pact_with_verification("Foo", "1", "Bar", "2")
46+
.create_pact_with_verification("Bar", "2", "Foo", "1")
47+
.add_day
48+
.create_pact_with_verification("Foo", "3", "Bar", "4")
49+
.create_pact_with_verification("Bar", "4", "Foo", "3")
50+
.add_day
51+
.create_pact_with_verification("Foo", "5", "Bar", "6")
52+
.create_pact_with_verification("Bar", "6", "Foo", "5")
53+
end
54+
let(:selectors) do
55+
[
56+
UnresolvedSelector.new(pacticipant_name: 'Foo'),
57+
UnresolvedSelector.new(pacticipant_name: 'Bar')
58+
]
59+
end
60+
let(:options) { { limit: 4 } }
61+
62+
subject { Repository.new.find(selectors, options) }
63+
64+
it "includes rows from each direction" do
65+
expect(subject.count{ |r| r.consumer_name == 'Foo' }).to eq (subject.count{ |r| r.consumer_name == 'Bar' })
66+
end
67+
68+
context "when where is a latestby" do
69+
let(:options) { { limit: 4, latestby: 'cvpv'} }
70+
71+
it "includes rows from each direction" do
72+
expect(subject.count{ |r| r.consumer_name == 'Foo' }).to eq (subject.count{ |r| r.consumer_name == 'Bar' })
73+
end
74+
end
75+
end
4276
end
4377
end
4478
end

spec/lib/pact_broker/matrix/repository_spec.rb

-9
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,6 @@ def shorten_rows rows
5353
let(:a1_c1_n1) { "A1 C1 n1" }
5454
let(:a2_b__n_) { "A2 B? n?" }
5555

56-
context "when a limit is specified" do
57-
let(:selectors) { build_selectors('A' => nil) }
58-
let(:options) { { limit: 1 } }
59-
60-
it "returns fewer rows than the limit (because some of the logic is done in the code, there may be fewer than the limit - need to fix this)" do
61-
expect(subject).to eq ["A2 B? n?"]
62-
end
63-
end
64-
6556
context "when just the consumer name is specified" do
6657
let(:selectors) { build_selectors('A' => nil) }
6758

0 commit comments

Comments
 (0)