Skip to content

Commit

Permalink
chore: fix logic for selecting "latest" pacts in dashboard.
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque committed Jan 29, 2018
1 parent 5914ae4 commit 413f999
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 20 deletions.
91 changes: 91 additions & 0 deletions db/migrations/20180129_create_latest_matrix_for_cv_and_pv.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
Sequel.migration do
up do
# Removes 'overwritten' pacts and verifications from the matrix
# (ie. only show latest pact revision for each consumer version and
# latest verification for each provider version)
# Must include lines where verification_id is null so that we don't
# lose the unverified pacts.
# In this view there will be one row for each consumer version/provider version
# This view used to be (stupidly) called latest_matrix
create_view(:latest_matrix_for_consumer_version_and_provider_version,
"SELECT matrix.* FROM matrix
INNER JOIN latest_verification_id_for_consumer_version_and_provider_version AS lv
ON ((matrix.consumer_version_id = lv.consumer_version_id)
AND (matrix.provider_version_id = lv.provider_version_id)
AND ((matrix.verification_id = lv.latest_verification_id)))
UNION
select matrix.* from matrix
inner join latest_pact_publication_revision_numbers lr
on matrix.consumer_id = lr.consumer_id
and matrix.provider_id = lr.provider_id
and matrix.consumer_version_order = lr.consumer_version_order
and matrix.pact_revision_number = lr.latest_revision_number
where verification_id is null
"
)

# it's a bit dodgey to be using the max(id) of the verification to determine the latest,
# but otherwise we'd need to add an extra step and find the latest_provider_version_order
# and then find the latest verification within that provider_version_order, which we'd
# probably do by using the ID anyway. And I'm feeling lazy.
create_view(:latest_verification_id_for_consumer_version_and_provider,
"select matrix.consumer_version_id, matrix.provider_id, max(verification_id) as latest_verification_id
from latest_matrix_for_consumer_version_and_provider_version matrix
where matrix.verification_id is not null
group by matrix.consumer_version_id, matrix.provider_id
"
)

# update the definition of latest_matrix to actually be the latest_matrix
# in the same way that latest_pact_publications is.
# It contains the latest verification results for the latest pacts.
create_or_replace_view(:latest_matrix,
"SELECT matrix.* FROM latest_matrix_for_consumer_version_and_provider_version matrix
INNER JOIN latest_pact_consumer_version_orders lpcvo
ON matrix.consumer_id = lpcvo.consumer_id
AND matrix.provider_id = lpcvo.provider_id
AND matrix.consumer_version_order = lpcvo.latest_consumer_version_order
INNER JOIN latest_verification_id_for_consumer_version_and_provider AS lv
ON ((matrix.consumer_version_id = lv.consumer_version_id)
AND (matrix.provider_id = lv.provider_id)
AND ((matrix.verification_id = lv.latest_verification_id)))
UNION
SELECT matrix.* FROM latest_matrix_for_consumer_version_and_provider_version matrix
INNER JOIN latest_pact_consumer_version_orders lpcvo
ON matrix.consumer_id = lpcvo.consumer_id
AND matrix.provider_id = lpcvo.provider_id
AND matrix.consumer_version_order = lpcvo.latest_consumer_version_order
where verification_id is null
"
)
end

down do
# revert to previous crappy definition
create_or_replace_view(:latest_matrix,
"SELECT matrix.* FROM matrix
INNER JOIN latest_verification_id_for_consumer_version_and_provider_version AS lv
ON ((matrix.consumer_version_id = lv.consumer_version_id)
AND (matrix.provider_version_id = lv.provider_version_id)
AND ((matrix.verification_id = lv.latest_verification_id)))
UNION
select matrix.* from matrix
inner join latest_pact_publication_revision_numbers lr
on matrix.consumer_id = lr.consumer_id
and matrix.provider_id = lr.provider_id
and matrix.consumer_version_order = lr.consumer_version_order
and matrix.pact_revision_number = lr.latest_revision_number
where verification_id is null
"
)

drop_view(:latest_verification_id_for_consumer_version_and_provider)
drop_view(:latest_matrix_for_consumer_version_and_provider_version)
end
end
4 changes: 2 additions & 2 deletions lib/pact_broker/index/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'pact_broker/logging'
require 'pact_broker/domain/index_item'
require 'pact_broker/matrix/latest_row'
require 'pact_broker/matrix/actual_latest_row'

module PactBroker

Expand All @@ -16,9 +17,8 @@ def self.find_index_items options = {}
rows = []

if !options[:tags]
rows = PactBroker::Matrix::LatestRow
rows = PactBroker::Matrix::ActualLatestRow
.select_all_qualified
.join(:latest_pact_publications, {consumer_id: :consumer_id, provider_id: :provider_id, consumer_version_order: :consumer_version_order})
.eager(:latest_triggered_webhooks)
.eager(:webhooks)
.order(:consumer_name, :provider_name)
Expand Down
24 changes: 24 additions & 0 deletions lib/pact_broker/matrix/actual_latest_row.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'pact_broker/matrix/row'

module PactBroker
module Matrix
# Latest pact revision for each consumer/provider => latest verification
class ActualLatestRow < Row
set_dataset(:latest_matrix)

# For some reason, with MySQL, the success column value
# comes back as an integer rather than a boolean
# for the latest_matrix view (but not the matrix view!)
# Maybe something to do with the union?
# Haven't investigated as this is an easy enough fix.
def success
value = super
value.nil? ? nil : value == true || value == 1
end

def values
super.merge(success: success)
end
end
end
end
4 changes: 2 additions & 2 deletions lib/pact_broker/matrix/latest_row.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

module PactBroker
module Matrix
# Latest pact revision for each consumer version => latest verification
# Latest pact revision for each consumer version => latest verification for each provider version
class LatestRow < Row
set_dataset(:latest_matrix)
set_dataset(:latest_matrix_for_consumer_version_and_provider_version)

# For some reason, with MySQL, the success column value
# comes back as an integer rather than a boolean
Expand Down
4 changes: 4 additions & 0 deletions lib/pact_broker/matrix/row.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ def compare_name_asc name1, name2
name1 <=> name2
end

def to_s
"#{consumer_name} v#{consumer_version_number} #{provider_name} #{provider_version_number} #{success}"
end

def compare_number_desc number1, number2
if number1 && number2
number2 <=> number1
Expand Down
15 changes: 15 additions & 0 deletions spec/lib/pact_broker/index/service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Index
let(:td) { TestDataBuilder.new }
let(:tags) { ['prod', 'production'] }
let(:options) { { tags: tags } }
let(:rows) { subject.find_index_items(options) }

subject{ Service }

Expand Down Expand Up @@ -110,6 +111,20 @@ module Index
expect(rows.first.latest_verification_latest_tags.collect(&:name)).to eq ['dev']
end
end

context "when there are multiple verifications for the latest consumer version" do
before do
td.create_pact_with_hierarchy("Foo", "1", "Bar")
.create_verification(provider_version: "1.0.0")
.create_verification(provider_version: "2.0.0", number: 2)
end

let(:options) { {} }

it "only returns the row for the latest provider version" do
expect(rows.count).to eq 1
end
end
end
end
end
Expand Down
100 changes: 84 additions & 16 deletions spec/migrations/50_create_latest_matrix_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
describe 'create latest matrix (latest pact revision/latest verification for provider version)', migration: true do
before do
PactBroker::Database.migrate(50)
PactBroker::Database.migrate(20180129)
end

def shorten_row row
Expand All @@ -9,18 +9,24 @@ def shorten_row row

let(:now) { DateTime.new(2018, 2, 2) }
let!(:consumer) { create(:pacticipants, {name: 'C', created_at: now, updated_at: now}) }
let!(:provider) { create(:pacticipants, {name: 'P', created_at: now, updated_at: now}) }
let!(:provider_1) { create(:pacticipants, {name: 'P', created_at: now, updated_at: now}) }
let!(:provider_2) { create(:pacticipants, {name: 'Q', created_at: now, updated_at: now}) }
let!(:consumer_version_1) { create(:versions, {number: '1', order: 1, pacticipant_id: consumer[:id], created_at: now, updated_at: now}) }
let!(:consumer_version_2) { create(:versions, {number: '2', order: 2, pacticipant_id: consumer[:id], created_at: now, updated_at: now}) }
let!(:provider_version_1) { create(:versions, {number: '1', order: 1, pacticipant_id: provider[:id], created_at: now, updated_at: now}) }
let!(:provider_version_2) { create(:versions, {number: '2', order: 2, pacticipant_id: provider[:id], created_at: now, updated_at: now}) }
let!(:pact_version_1) { create(:pact_versions, {content: {some: 'json'}.to_json, sha: '1', consumer_id: consumer[:id], provider_id: provider[:id], created_at: now}) }
let!(:pact_version_2) { create(:pact_versions, {content: {some: 'json other'}.to_json, sha: '2', consumer_id: consumer[:id], provider_id: provider[:id], created_at: now}) }
let!(:pact_version_3) { create(:pact_versions, {content: {some: 'json more'}.to_json, sha: '3', consumer_id: consumer[:id], provider_id: provider[:id], created_at: now}) }
let!(:provider_1_version_1) { create(:versions, {number: '1', order: 1, pacticipant_id: provider_1[:id], created_at: now, updated_at: now}) }
let!(:provider_1_version_2) { create(:versions, {number: '2', order: 2, pacticipant_id: provider_1[:id], created_at: now, updated_at: now}) }
let!(:provider_2_version_1) { create(:versions, {number: '1', order: 1, pacticipant_id: provider_2[:id], created_at: now, updated_at: now}) }
let!(:provider_2_version_2) { create(:versions, {number: '2', order: 2, pacticipant_id: provider_2[:id], created_at: now, updated_at: now}) }

let!(:pact_version_1) { create(:pact_versions, {content: {some: 'json'}.to_json, sha: '1', consumer_id: consumer[:id], provider_id: provider_1[:id], created_at: now}) }
let!(:pact_version_2) { create(:pact_versions, {content: {some: 'json other'}.to_json, sha: '2', consumer_id: consumer[:id], provider_id: provider_1[:id], created_at: now}) }
let!(:pact_version_3) { create(:pact_versions, {content: {some: 'json more'}.to_json, sha: '3', consumer_id: consumer[:id], provider_id: provider_1[:id], created_at: now}) }
let!(:pact_version_4) { create(:pact_versions, {content: {some: 'json more blah'}.to_json, sha: '4', consumer_id: consumer[:id], provider_id: provider_2[:id], created_at: now}) }

let!(:pact_publication_1) do
create(:pact_publications, {
consumer_version_id: consumer_version_1[:id],
provider_id: provider[:id],
provider_id: provider_1[:id],
revision_number: 1,
pact_version_id: pact_version_1[:id],
created_at: now
Expand All @@ -30,7 +36,7 @@ def shorten_row row
let!(:pact_publication_2) do
create(:pact_publications, {
consumer_version_id: consumer_version_1[:id],
provider_id: provider[:id],
provider_id: provider_1[:id],
revision_number: 2,
pact_version_id: pact_version_2[:id],
created_at: now
Expand All @@ -41,19 +47,30 @@ def shorten_row row
let!(:pact_publication_3) do
create(:pact_publications, {
consumer_version_id: consumer_version_2[:id],
provider_id: provider[:id],
provider_id: provider_1[:id],
revision_number: 1,
pact_version_id: pact_version_3[:id],
created_at: now
})
end

# C2 Q1 (r1/n1)
let!(:pact_publication_4) do
create(:pact_publications, {
consumer_version_id: consumer_version_2[:id],
provider_id: provider_2[:id],
revision_number: 1,
pact_version_id: pact_version_4[:id],
created_at: now
})
end

# C1 P3 (r1n3)
let!(:verification_1) do
create(:verifications, {
number: 1,
success: true,
provider_version_id: provider_version_1[:id],
provider_version_id: provider_1_version_1[:id],
pact_version_id: pact_version_1[:id],
execution_date: now,
created_at: now
Expand All @@ -65,7 +82,7 @@ def shorten_row row
create(:verifications, {
number: 1,
success: true,
provider_version_id: provider_version_1[:id],
provider_version_id: provider_1_version_1[:id],
pact_version_id: pact_version_2[:id],
execution_date: now,
created_at: now
Expand All @@ -77,7 +94,7 @@ def shorten_row row
create(:verifications, {
number: 2,
success: true,
provider_version_id: provider_version_1[:id],
provider_version_id: provider_1_version_1[:id],
pact_version_id: pact_version_2[:id],
execution_date: now,
created_at: now
Expand All @@ -89,25 +106,76 @@ def shorten_row row
create(:verifications, {
number: 3,
success: true,
provider_version_id: provider_version_2[:id],
provider_version_id: provider_1_version_2[:id],
pact_version_id: pact_version_2[:id],
execution_date: now,
created_at: now
})
end

# include
# C1 Q1 (r1/n1)
let!(:verification_5) do
create(:verifications, {
number: 1,
success: true,
provider_version_id: provider_2_version_1[:id],
pact_version_id: pact_version_4[:id],
execution_date: now,
created_at: now
})
end

# include
# C1 Q2 (r1/n2)
let!(:verification_6) do
create(:verifications, {
number: 2,
success: true,
provider_version_id: provider_2_version_2[:id],
pact_version_id: pact_version_4[:id],
execution_date: now,
created_at: now
})
end

# C1 P1 (r1/n1) this pact version is overwritten by r2
# C1 P1 (r2/n1) this verification is overwritten by n2
# C1 P1 (r2/n2)
# C1 P2 (r2/n3)
# C2 P? (r1/n?)

describe "matrix" do
it "includes every revision and every verification" do
rows = database[:matrix].order(:verification_id).all.collect{|row| shorten_row(row) }
expect(rows).to include "C1 P1 (r1/n1)"
expect(rows).to include "C1 P1 (r2/n1)"
expect(rows.count).to eq 7
end
end

it "only includes the latest pact revisions and latest verifications" do
rows = database[:latest_matrix].order(:verification_id).all.collect{|row| shorten_row(row) }
rows = database[:latest_matrix_for_consumer_version_and_provider_version].order(:verification_id).all.collect{|row| shorten_row(row) }
expect(rows).to include "C1 P1 (r2/n2)"
expect(rows).to include "C1 P2 (r2/n3)"
expect(rows).to include "C2 P? (r1/n?)"
expect(rows).to include "C2 Q1 (r1/n1)"
expect(rows).to include "C2 Q2 (r1/n2)"
expect(rows).to_not include "C1 P1 (r1/n1)"
expect(rows).to_not include "C1 P1 (r2/n1)"
expect(database[:latest_matrix].count).to eq 3
expect(database[:latest_matrix_for_consumer_version_and_provider_version].count).to eq 5
end

describe "latest matrix" do
it "only includes the latest pact revisions and latest verifications for the latest consumer versions" do
rows = database[:latest_matrix].order(:verification_id).all.collect{|row| shorten_row(row) }
expect(rows).to include "C2 P? (r1/n?)"
expect(rows).to include "C2 Q2 (r1/n2)"
expect(rows).to_not include "C2 Q1 (r1/n1)" # not latest provider version
expect(rows).to_not include "C1 P1 (r2/n2)" # not latest consumer version
expect(rows).to_not include "C1 P2 (r2/n3)" # not latest consumer version
expect(rows).to_not include "C1 P1 (r2/n1)" # not latest consumer version
expect(database[:latest_matrix].count).to eq 2
end
end
end

0 comments on commit 413f999

Please sign in to comment.