Skip to content

Commit 6c8e38f

Browse files
committed
fix: duplicate key value violates unique constraint "cv_prov_revision_unq" error when publishing identical pact resources at the same time
1 parent df7c836 commit 6c8e38f

File tree

4 files changed

+77
-12
lines changed

4 files changed

+77
-12
lines changed

lib/pact_broker/pacts/pact_publication.rb

+7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
module PactBroker
66
module Pacts
77
class PactPublication < Sequel::Model(:pact_publications)
8+
UNIQUE_CONSTRAINT_KEYS = [:consumer_version_id, :provider_id, :revision_number].freeze
89

910
extend Forwardable
1011

@@ -57,6 +58,12 @@ def to_version_domain
5758
OpenStruct.new(number: consumer_version.number, pacticipant: consumer_version.pacticipant, tags: consumer_version.tags, order: consumer_version.order)
5859
end
5960

61+
def upsert
62+
params = to_hash.merge(created_at: Sequel.datetime_class.now)
63+
self.id = PactPublication.upsert(params, UNIQUE_CONSTRAINT_KEYS).id
64+
self.refresh
65+
end
66+
6067
private
6168

6269
def cached_domain_for_delegation

lib/pact_broker/pacts/repository.rb

+5-8
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ def create params
3131
consumer_version_id: params[:version_id],
3232
provider_id: params[:provider_id],
3333
consumer_id: params[:consumer_id],
34-
pact_version: pact_version
35-
).save
34+
pact_version: pact_version,
35+
revision_number: 1
36+
).upsert
3637
update_latest_pact_publication_ids(pact_publication)
3738
pact_publication.to_domain
3839
end
@@ -46,18 +47,14 @@ def update id, params
4647
params.fetch(:json_content)
4748
)
4849
if existing_model.pact_version_id != pact_version.id
49-
key = {
50+
pact_publication = PactPublication.new(
5051
consumer_version_id: existing_model.consumer_version_id,
5152
provider_id: existing_model.provider_id,
5253
revision_number: next_revision_number(existing_model),
53-
}
54-
new_params = key.merge(
5554
consumer_id: existing_model.consumer_id,
5655
pact_version_id: pact_version.id,
5756
created_at: Sequel.datetime_class.now
58-
)
59-
PactPublication.upsert(new_params, key.keys)
60-
pact_publication = PactPublication.where(key).single_record
57+
).upsert
6158
update_latest_pact_publication_ids(pact_publication)
6259
pact_publication.to_domain
6360
else

lib/pact_broker/repositories/helpers.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,22 @@ def select_for_subquery column
3636
end
3737
end
3838

39-
def upsert row, key_names, columns_to_update = nil
39+
def upsert row, unique_key_names, columns_to_update = nil
4040
if postgres?
41-
insert_conflict(update: row, target: key_names).insert(row)
41+
insert_conflict(update: row, target: unique_key_names).insert(row)
4242
elsif mysql?
43-
update_cols = columns_to_update || (row.keys - key_names)
43+
update_cols = columns_to_update || (row.keys - unique_key_names)
4444
on_duplicate_key_update(*update_cols).insert(row)
4545
else
4646
# Sqlite
47-
key = row.reject{ |k, v| !key_names.include?(k) }
47+
key = row.reject{ |k, v| !unique_key_names.include?(k) }
4848
if where(key).count == 0
4949
insert(row)
5050
else
5151
where(key).update(row)
5252
end
5353
end
54+
model.find(row.select{ |key, _| unique_key_names.include?(key)} )
5455
end
5556
end
5657
end

spec/lib/pact_broker/pacts/pact_publication_spec.rb

+60
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,66 @@
33
module PactBroker
44
module Pacts
55
describe PactPublication do
6+
describe "save and upsert" do
7+
before do
8+
td.create_consumer
9+
.create_provider
10+
.create_consumer_version
11+
.create_pact
12+
end
13+
14+
let(:params) do
15+
{
16+
consumer_id: td.consumer.id,
17+
provider_id: td.provider.id,
18+
consumer_version_id: td.consumer_version.id,
19+
pact_version_id: PactVersion.first.id,
20+
revision_number: 1
21+
}
22+
end
23+
24+
let(:pact_publication) do
25+
PactPublication.new(params)
26+
end
27+
28+
context "when using a PactPublication with the same provider/consumer version/revision number as an existing PactPublication" do
29+
describe "save" do
30+
it "raises a constraint exception" do
31+
expect { pact_publication.save }.to raise_error Sequel::UniqueConstraintViolation
32+
end
33+
end
34+
35+
describe "upsert" do
36+
it "does not raise an error" do
37+
pact_publication.upsert
38+
end
39+
40+
it "sets the relationship objects" do
41+
pact_publication.upsert
42+
expect(pact_publication.id).to_not be nil
43+
expect(pact_publication.consumer.id).to eq td.consumer.id
44+
expect(pact_publication.consumer.name).to eq td.consumer.name
45+
end
46+
47+
context "with objects instead of ids" do
48+
let(:params) do
49+
{
50+
consumer: td.consumer,
51+
provider: td.provider,
52+
consumer_version: td.consumer_version,
53+
pact_version: PactVersion.first,
54+
revision_number: 1
55+
}
56+
end
57+
58+
it "also works" do
59+
pact_publication.upsert
60+
expect(pact_publication.consumer_id).to eq td.consumer.id
61+
end
62+
end
63+
end
64+
end
65+
end
666

767
describe "#latest_tag_names" do
868
before do

0 commit comments

Comments
 (0)