diff --git a/db/migrations/20211101_recreate_all_verifications.rb b/db/migrations/20211101_recreate_all_verifications.rb new file mode 100644 index 000000000..eb5b9c94a --- /dev/null +++ b/db/migrations/20211101_recreate_all_verifications.rb @@ -0,0 +1,25 @@ +Sequel.migration do + up do + # need to fully qualify build_url because versions now has a build_url too. + # We don't use this view any more but we get an error when dropping the integrations + # view if we don't update this, so it must force some re-calculation to be done. + create_or_replace_view(:all_verifications, + from(:verifications).select( + Sequel[:verifications][:id], + Sequel[:verifications][:number], + :success, + :provider_version_id, + Sequel[:v][:number].as(:provider_version_number), + Sequel[:v][:order].as(:provider_version_order), + Sequel[:verifications][:build_url], + :pact_version_id, + :execution_date, + Sequel[:verifications][:created_at], + :test_results + ).join(:versions, {id: :provider_version_id}, {:table_alias => :v}) + ) + end + + down do + end +end diff --git a/db/migrations/20211102_create_table_temp_integrations.rb b/db/migrations/20211102_create_table_temp_integrations.rb new file mode 100644 index 000000000..434e4fd89 --- /dev/null +++ b/db/migrations/20211102_create_table_temp_integrations.rb @@ -0,0 +1,23 @@ +Sequel.migration do + up do + create_table(:temp_integrations, charset: "utf8") do + primary_key :id + foreign_key(:consumer_id, :pacticipants, null: false, on_delete: :cascade, foreign_key_constraint_name: "integrations_consumer_id_foreign_key") + foreign_key(:provider_id, :pacticipants, null: false, on_delete: :cascade, foreign_key_constraint_name: "integrations_provider_id_foreign_key") + String :consumer_name + String :provider_name + DateTime :created_at, null: false + end + + # TODO drop these columns + # They are just for backwards compatiblity during schema migrations + # alter_table(:integrations) do + # drop_column(:consumer_name) + # drop_column(:provider_name) + # end + end + + down do + drop_table(:temp_integrations) + end +end diff --git a/db/migrations/20211103_migrate_integrations.rb b/db/migrations/20211103_migrate_integrations.rb new file mode 100644 index 000000000..5c287b50f --- /dev/null +++ b/db/migrations/20211103_migrate_integrations.rb @@ -0,0 +1,21 @@ +Sequel.migration do + up do + from(:temp_integrations).insert( + [:consumer_id, :consumer_name, :provider_id, :provider_name, :created_at], + from(:pact_publications) + .select( + :consumer_id, + Sequel[:c][:name].as(:consumer_name), + :provider_id, + Sequel[:p][:name].as(:provider_name), + Sequel[:c][:created_at] + ).distinct + .join(:pacticipants, {:id => :consumer_id}, {:table_alias => :c, implicit_qualifier: :pact_publications}) + .join(:pacticipants, {:id => :provider_id}, {:table_alias => :p, implicit_qualifier: :pact_publications}) + ) + end + + down do + + end +end diff --git a/db/migrations/20211104_switch_integrations_and_temp_integrations.rb b/db/migrations/20211104_switch_integrations_and_temp_integrations.rb new file mode 100644 index 000000000..8d59f0293 --- /dev/null +++ b/db/migrations/20211104_switch_integrations_and_temp_integrations.rb @@ -0,0 +1,25 @@ +Sequel.migration do + up do + transaction do + drop_view :integrations + rename_table :temp_integrations, :integrations + end + end + + down do + transaction do + rename_table :integrations, :temp_integrations + create_view(:integrations, + from(:pact_publications) + .select( + :consumer_id, + Sequel[:c][:name].as(:consumer_name), + :provider_id, + Sequel[:p][:name].as(:provider_name) + ).distinct + .join(:pacticipants, {:id => :consumer_id}, {:table_alias => :c, implicit_qualifier: :pact_publications}) + .join(:pacticipants, {:id => :provider_id}, {:table_alias => :p, implicit_qualifier: :pact_publications}) + ) + end + end +end diff --git a/lib/pact_broker/integrations/integration.rb b/lib/pact_broker/integrations/integration.rb index 3e5a7bafe..97659ff12 100644 --- a/lib/pact_broker/integrations/integration.rb +++ b/lib/pact_broker/integrations/integration.rb @@ -6,8 +6,8 @@ module PactBroker module Integrations - class Integration < Sequel::Model - set_primary_key [:consumer_id, :provider_id] + class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id)) + plugin :insert_ignore, identifying_columns: [:consumer_id, :provider_id] associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id) associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id) associate(:one_to_one, :latest_verification, :class => "PactBroker::Verifications::LatestVerificationForConsumerAndProvider", key: [:consumer_id, :provider_id], primary_key: [:consumer_id, :provider_id]) @@ -18,7 +18,7 @@ class Integration < Sequel::Model # Update: now we have pagination, we should probably filter the pacts by consumer/provider id. LATEST_PACT_EAGER_LOADER = proc do |eo_opts| eo_opts[:rows].each do |integration| - integration.associations[:latest_pact] = [] + integration.associations[:latest_pact] = nil end PactBroker::Pacts::PactPublication.overall_latest.each do | pact | @@ -58,7 +58,7 @@ def verification_status_for_latest_pact end def latest_pact_or_verification_publication_date - [latest_pact.created_at, latest_verification_publication_date].compact.max + [latest_pact&.created_at, latest_verification_publication_date].compact.max end def latest_verification_publication_date @@ -68,6 +68,14 @@ def latest_verification_publication_date def <=>(other) [consumer.name.downcase, provider.name.downcase] <=> [other.consumer.name.downcase, other.provider.name.downcase] end + + def consumer_name + consumer.name + end + + def provider_name + provider.name + end end end end diff --git a/lib/pact_broker/integrations/repository.rb b/lib/pact_broker/integrations/repository.rb new file mode 100644 index 000000000..f176c08bb --- /dev/null +++ b/lib/pact_broker/integrations/repository.rb @@ -0,0 +1,22 @@ +require "pact_broker/integrations/integration" + +module PactBroker + module Integrations + class Repository + def create_for_pact(consumer_id, provider_id) + if Integration.where(consumer_id: consumer_id, provider_id: provider_id).empty? + Integration.new( + consumer_id: consumer_id, + provider_id: provider_id, + created_at: Sequel.datetime_class.now + ).insert_ignore + end + nil + end + + def delete(consumer_id, provider_id) + Integration.where(consumer_id: consumer_id, provider_id: provider_id).delete + end + end + end +end diff --git a/lib/pact_broker/integrations/service.rb b/lib/pact_broker/integrations/service.rb index 201de8a95..68f002d5f 100644 --- a/lib/pact_broker/integrations/service.rb +++ b/lib/pact_broker/integrations/service.rb @@ -41,7 +41,7 @@ def self.delete(consumer_name, provider_name) pact_service.delete_all_pact_versions_between(consumer_name, and: provider_name) webhook_repository.delete_by_consumer_and_provider(consumer, provider) version_repository.delete_orphan_versions(consumer, provider) - + integration_repository.delete(consumer.id, provider.id) pacticipant_service.delete_if_orphan(consumer) pacticipant_service.delete_if_orphan(provider) unless consumer == provider end diff --git a/lib/pact_broker/pacticipants/repository.rb b/lib/pact_broker/pacticipants/repository.rb index d939107b4..e8a49ccf1 100644 --- a/lib/pact_broker/pacticipants/repository.rb +++ b/lib/pact_broker/pacticipants/repository.rb @@ -77,9 +77,14 @@ def replace(_pacticipant_name, open_struct_pacticipant) ).save end + def delete(pacticipant) + pacticipant.destroy + end + def pacticipant_names PactBroker::Domain::Pacticipant.select(:name).order(:name).collect(&:name) end + def delete_if_orphan(pacticipant) if PactBroker::Domain::Version.where(pacticipant: pacticipant).empty? && PactBroker::Pacts::PactPublication.where(provider: pacticipant).or(consumer: pacticipant).empty? && diff --git a/lib/pact_broker/pacticipants/service.rb b/lib/pact_broker/pacticipants/service.rb index 880505d32..84c79d2ee 100644 --- a/lib/pact_broker/pacticipants/service.rb +++ b/lib/pact_broker/pacticipants/service.rb @@ -79,7 +79,7 @@ def self.replace(pacticipant_name, open_struct_pacticipant) def self.delete(name) pacticipant = find_pacticipant_by_name name webhook_service.delete_all_webhhook_related_objects_by_pacticipant(pacticipant) - pacticipant.destroy + pacticipant_repository.delete(pacticipant) end def self.delete_if_orphan(pacticipant) diff --git a/lib/pact_broker/pacts/repository.rb b/lib/pact_broker/pacts/repository.rb index ffc0a4768..df8714fb7 100644 --- a/lib/pact_broker/pacts/repository.rb +++ b/lib/pact_broker/pacts/repository.rb @@ -37,6 +37,7 @@ def unscoped(scope) end def create params + integration_repository.create_for_pact(params.fetch(:consumer_id), params.fetch(:provider_id)) pact_version = find_or_create_pact_version( params.fetch(:consumer_id), params.fetch(:provider_id), diff --git a/lib/pact_broker/repositories.rb b/lib/pact_broker/repositories.rb index 992184785..cf47758b3 100644 --- a/lib/pact_broker/repositories.rb +++ b/lib/pact_broker/repositories.rb @@ -47,6 +47,11 @@ def branch_version_repository PactBroker::Versions::BranchVersionRepository.new end + def integration_repository + require "pact_broker/integrations/repository" + PactBroker::Integrations::Repository.new + end + extend self end end diff --git a/lib/pact_broker/test/test_data_builder.rb b/lib/pact_broker/test/test_data_builder.rb index 6c502aab6..6c7a4f472 100644 --- a/lib/pact_broker/test/test_data_builder.rb +++ b/lib/pact_broker/test/test_data_builder.rb @@ -30,6 +30,7 @@ require "pact_broker/deployments/deployed_version_service" require "pact_broker/deployments/released_version_service" require "pact_broker/versions/branch_version_repository" +require "pact_broker/integrations/repository" require "ostruct" module PactBroker @@ -157,6 +158,11 @@ def create_provider provider_name = "Provider #{model_counter}", params = {} self end + def create_integration + PactBroker::Integrations::Repository.new.create_for_pact(consumer.id, provider.id) + self + end + def use_provider provider_name @provider = PactBroker::Domain::Pacticipant.find(:name => provider_name) self diff --git a/spec/lib/pact_broker/integrations/integration_spec.rb b/spec/lib/pact_broker/integrations/integration_spec.rb index 369dd08ae..c700a2ea6 100644 --- a/spec/lib/pact_broker/integrations/integration_spec.rb +++ b/spec/lib/pact_broker/integrations/integration_spec.rb @@ -19,19 +19,19 @@ module Integrations end it "has a relationship to the latest pact (eager)" do - integrations = Integration.eager(:latest_pact).order(:consumer_name, :provider_name).all + integrations = Integration.eager(:latest_pact).order(Sequel.desc(:id)).all expect(integrations.first.latest_pact.consumer_version_number).to eq "2" expect(integrations.last.latest_pact.consumer_version_number).to eq "1" end it "has a relationship to the latest pact (not eager)" do - integrations = Integration.order(:consumer_name, :provider_name).all + integrations = Integration.order(Sequel.desc(:id)).all expect(integrations.first.latest_pact.consumer_version_number).to eq "2" expect(integrations.last.latest_pact.consumer_version_number).to eq "1" end it "has a relationship to the latest verification via the latest pact" do - integration = Integration.eager(latest_pact: :latest_verification).order(:consumer_name, :provider_name).all.first + integration = Integration.eager(latest_pact: :latest_verification).order(Sequel.desc(:id)).all.first expect(integration.latest_pact.latest_verification.provider_version_number).to eq "4" end @@ -40,12 +40,12 @@ module Integrations end it "has a latest verification - this may not be the same as the latest verification for the latest pact" do - integration = Integration.eager(:latest_verification).order(:consumer_name, :provider_name).all.first + integration = Integration.eager(:latest_verification).order(Sequel.desc(:id)).all.first expect(integration.latest_verification.provider_version_number).to eq "4" end describe "latest_pact_or_verification_publication_date" do - let(:first_integration) { Integration.order(:consumer_name, :provider_name).first } + let(:first_integration) { Integration.order(:id).last } context "when the last publication is a verification" do it "returns the verification execution date" do @@ -65,6 +65,18 @@ module Integrations expect(first_integration.latest_pact_or_verification_publication_date.to_datetime).to eq date end end + + context "when there is no pact or verification" do + before do + td.create_consumer("orphan-consumer") + .create_provider("orphan-provider") + .create_integration + end + + it "returns nil" do + expect(Integration.last.latest_pact_or_verification_publication_date).to be nil + end + end end end @@ -80,7 +92,7 @@ module Integrations end it "returns a list of triggered webhooks" do - integrations = Integration.eager(:latest_triggered_webhooks).order(:consumer_name, :provider_name).all + integrations = Integration.eager(:latest_triggered_webhooks).order(Sequel.desc(:id)).all expect(integrations.first.latest_triggered_webhooks.count).to eq 1 end end @@ -99,7 +111,7 @@ module Integrations end it "returns all the webhooks" do - integrations = Integration.eager(:webhooks).order(:consumer_name, :provider_name).all + integrations = Integration.eager(:webhooks).order(Sequel.desc(:id)).all expect(integrations.first.webhooks.count).to eq 3 end end diff --git a/spec/lib/pact_broker/integrations/service_spec.rb b/spec/lib/pact_broker/integrations/service_spec.rb index 1ea29ed18..76a29f413 100644 --- a/spec/lib/pact_broker/integrations/service_spec.rb +++ b/spec/lib/pact_broker/integrations/service_spec.rb @@ -33,6 +33,16 @@ module Integrations describe "#delete" do subject { Service.delete("Foo", "Bar") } + describe "the integration" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + end + + it "deletes it" do + expect{ subject }.to change { Integration.count }.by(-1) + end + end + context "with webhook data" do before do td.create_pact_with_hierarchy("Foo", "1", "Bar") diff --git a/spec/lib/pact_broker/pacticipants/repository_spec.rb b/spec/lib/pact_broker/pacticipants/repository_spec.rb index 78ff9d341..23505d29c 100644 --- a/spec/lib/pact_broker/pacticipants/repository_spec.rb +++ b/spec/lib/pact_broker/pacticipants/repository_spec.rb @@ -69,6 +69,18 @@ module Pacticipants end end + describe "delete" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + end + + subject { Repository.new.delete(td.consumer) } + + it "deletes the integration" do + expect { subject }.to change{ PactBroker::Integrations::Integration.count }.by(-1) + end + end + describe "#find" do before do td diff --git a/spec/lib/pact_broker/pacts/repository_spec.rb b/spec/lib/pact_broker/pacts/repository_spec.rb index 4a7e3dfc0..5c88d3c95 100644 --- a/spec/lib/pact_broker/pacts/repository_spec.rb +++ b/spec/lib/pact_broker/pacts/repository_spec.rb @@ -73,6 +73,12 @@ module Pacts expect(PactVersion.order(:id).last.messages_count).to eq 0 end + it "creates an integration" do + expect { subject }.to change { + PactBroker::Integrations::Integration.where(consumer_id: consumer.id, provider_id: provider.id).count + }.from(0).to(1) + end + context "when a pact already exists with exactly the same content" do let(:another_version) { Versions::Repository.new.create number: "2.0.0", pacticipant_id: consumer.id }