From 8b0ce4d2f2699a97ff5e4e56ee9e6b57ab9bc856 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 15 Jun 2023 13:50:08 +1000 Subject: [PATCH 01/16] feat: add contract_data_updated_at to integrations table to speed up dashboard query PACT-1070 --- ...d_integrations_contract_data_updated_at.rb | 13 ++++ lib/pact_broker/api.rb | 6 ++ lib/pact_broker/events/subscriber.rb | 16 +++-- .../integrations/event_listener.rb | 21 +++++++ lib/pact_broker/integrations/integration.rb | 2 +- lib/pact_broker/integrations/repository.rb | 6 ++ ...h_verification_results_and_version_spec.rb | 18 ------ .../endpoints/publish_contracts_spec.rb | 6 ++ .../publish_verification_results_spec.rb | 52 ++++++++++++++++ .../integrations/repository_spec.rb | 61 +++++++++++++++++++ 10 files changed, 178 insertions(+), 23 deletions(-) create mode 100644 db/migrations/20230615_add_integrations_contract_data_updated_at.rb create mode 100644 lib/pact_broker/integrations/event_listener.rb create mode 100644 spec/integration/endpoints/publish_verification_results_spec.rb create mode 100644 spec/lib/pact_broker/integrations/repository_spec.rb diff --git a/db/migrations/20230615_add_integrations_contract_data_updated_at.rb b/db/migrations/20230615_add_integrations_contract_data_updated_at.rb new file mode 100644 index 000000000..6077b3a5d --- /dev/null +++ b/db/migrations/20230615_add_integrations_contract_data_updated_at.rb @@ -0,0 +1,13 @@ +Sequel.migration do + up do + alter_table(:integrations) do + add_column(:contract_data_updated_at, DateTime) + end + end + + down do + alter_table(:integrations) do + drop_column(:contract_data_updated_at) + end + end +end diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index 56c635dad..57a782fc8 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -23,6 +23,12 @@ def really_put? end end +# Not sure where to put this - it's a global subscription +require "pact_broker/events/subscriber" +require "pact_broker/integrations/event_listener" + +PactBroker::Events.subscribe(PactBroker::Integrations::EventListener.new) + module PactBroker # rubocop: disable Metrics/MethodLength def self.build_api(application_context = PactBroker::ApplicationContext.default_application_context) diff --git a/lib/pact_broker/events/subscriber.rb b/lib/pact_broker/events/subscriber.rb index 5b144593c..ca3e2a961 100644 --- a/lib/pact_broker/events/subscriber.rb +++ b/lib/pact_broker/events/subscriber.rb @@ -32,11 +32,19 @@ module Events extend self def subscribe(*args) - result = nil - TemporaryListeners.subscribe(*args) do - result = yield + if block_given? + result = nil + TemporaryListeners.subscribe(*args) do + result = yield + end + result + else + Wisper.subscribe(*args) end - result + end + + def unsubscribe(*args) + Wisper.unsubscribe(*args) end end end diff --git a/lib/pact_broker/integrations/event_listener.rb b/lib/pact_broker/integrations/event_listener.rb new file mode 100644 index 000000000..8f002524e --- /dev/null +++ b/lib/pact_broker/integrations/event_listener.rb @@ -0,0 +1,21 @@ +require "pact_broker/services" + +module PactBroker + module Integrations + class EventListener + include PactBroker::Services + + # @param [Hash] params the params from the broadcast event + # @option params [PactBroker::Domain::Pact] :pact the newly published pact + def contract_published(params) + integration_service.handle_contract_data_published(params.fetch(:pact).consumer, params.fetch(:pact).provider) + end + + # @param [Hash] params the params from the broadcast event + # @option params [PactBroker::Domain::Verification] :verification the newly published verification + def provider_verification_published(params) + integration_service.handle_contract_data_published(params.fetch(:verification).consumer, params.fetch(:verification).provider) + end + end + end +end diff --git a/lib/pact_broker/integrations/integration.rb b/lib/pact_broker/integrations/integration.rb index cc288d701..93e46a7a2 100644 --- a/lib/pact_broker/integrations/integration.rb +++ b/lib/pact_broker/integrations/integration.rb @@ -6,7 +6,7 @@ module PactBroker module Integrations - class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id)) + class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id, :contract_data_updated_at)) set_primary_key :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) diff --git a/lib/pact_broker/integrations/repository.rb b/lib/pact_broker/integrations/repository.rb index f176c08bb..b5b1a99c6 100644 --- a/lib/pact_broker/integrations/repository.rb +++ b/lib/pact_broker/integrations/repository.rb @@ -17,6 +17,12 @@ def create_for_pact(consumer_id, provider_id) def delete(consumer_id, provider_id) Integration.where(consumer_id: consumer_id, provider_id: provider_id).delete end + + def set_contract_data_updated_at(consumer, provider) + Integration + .where({ consumer_id: consumer&.id, provider_id: provider.id }.compact ) + .update(contract_data_updated_at: Sequel.datetime_class.now) + end end end end diff --git a/spec/features/publish_verification_results_and_version_spec.rb b/spec/features/publish_verification_results_and_version_spec.rb index ecf4a75b6..ae3ae3735 100644 --- a/spec/features/publish_verification_results_and_version_spec.rb +++ b/spec/features/publish_verification_results_and_version_spec.rb @@ -49,22 +49,4 @@ expect(last_response.status).to be 200 expect(JSON.parse(subject.body)).to include JSON.parse(verification_content) end - - context "with a webhook configured", job: true do - before do - td.create_webhook( - method: "POST", - url: "http://example.org", - events: [{ name: PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED }] - ) - end - let!(:request) do - stub_request(:post, "http://example.org").to_return(:status => 200) - end - - it "executes the webhook" do - subject - expect(request).to have_been_made - end - end end diff --git a/spec/integration/endpoints/publish_contracts_spec.rb b/spec/integration/endpoints/publish_contracts_spec.rb index 6febe86ba..9bea43c4b 100644 --- a/spec/integration/endpoints/publish_contracts_spec.rb +++ b/spec/integration/endpoints/publish_contracts_spec.rb @@ -21,6 +21,7 @@ let(:rack_headers) { { "CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/hal+json" } } let(:encoded_contract) { Base64.strict_encode64(contract) } let(:path) { "/contracts/publish" } + let(:contract) { td.fixed_json_content("Foo", "Bar", "1") } subject { post(path, request_body_hash.to_json, rack_headers) } @@ -30,4 +31,9 @@ its(:status) { is_expected.to eq 400 } its(:body) { is_expected.to include("non UTF-8 character") } end + + it "sets the contract_data_updated_at on the integration" do + subject + expect(PactBroker::Integrations::Integration.last.contract_data_updated_at).to_not be nil + end end diff --git a/spec/integration/endpoints/publish_verification_results_spec.rb b/spec/integration/endpoints/publish_verification_results_spec.rb new file mode 100644 index 000000000..dfc74bd3b --- /dev/null +++ b/spec/integration/endpoints/publish_verification_results_spec.rb @@ -0,0 +1,52 @@ +require "pact_broker/domain/verification" +require "timecop" + +describe "Publishing a pact verification" do + let(:path) { "/pacts/provider/Provider/consumer/Consumer/pact-version/#{pact.pact_version_sha}/verification-results" } + let(:verification_content) { load_fixture("verification.json") } + let(:parsed_response_body) { JSON.parse(subject.body) } + let(:pact) { td.pact } + let(:rack_env) do + { + "CONTENT_TYPE" => "application/json", + "HTTP_ACCEPT" => "application/hal+json", + "pactbroker.database_connector" => lambda { |&block| block.call } + } + end + + subject { post(path, verification_content, rack_env) } + + before do + Timecop.freeze(Date.today - 2) do + td.create_provider("Provider") + .create_consumer("Consumer") + .create_consumer_version("1.0.0") + .create_pact + .create_consumer_version("1.2.3") + .create_pact + .revise_pact + end + end + + it "updates the contract_data_updated_at on the integration" do + expect { subject }.to change { PactBroker::Integrations::Integration.last.contract_data_updated_at } + end + + context "with a webhook configured", job: true do + before do + td.create_webhook( + method: "POST", + url: "http://example.org", + events: [{ name: PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED }] + ) + end + let!(:request) do + stub_request(:post, "http://example.org").to_return(:status => 200) + end + + it "executes the webhook" do + subject + expect(request).to have_been_made + end + end +end diff --git a/spec/lib/pact_broker/integrations/repository_spec.rb b/spec/lib/pact_broker/integrations/repository_spec.rb new file mode 100644 index 000000000..d24a0ea69 --- /dev/null +++ b/spec/lib/pact_broker/integrations/repository_spec.rb @@ -0,0 +1,61 @@ +require "pact_broker/integrations/repository" +require "timecop" + +module PactBroker + module Integrations + describe Repository do + describe "#set_contract_data_updated_at" do + before do + # A -> B + # Foo -> Bar + td.create_consumer("A") + .create_provider("B") + .create_integration + .create_consumer("Foo") + .create_provider("Bar") + .create_integration + end + + let(:then) { Date.today - 20 } + let(:now) { DateTime.new(2010, 11, 1, 1, 1, 1) } + + subject do + Timecop.freeze(now) do + Repository.new.set_contract_data_updated_at(td.and_return(:consumer), td.and_return(:provider)) + end + end + + it "updates the contract_data_updated_at to now" do + expect { subject }.to change { Integration.last.contract_data_updated_at }.from(nil).to(now) + end + + it "does not update the other integrations" do + expect { subject }.to_not change { Integration.first.contract_data_updated_at } + end + + context "with the consumer is nil (eg. when a provider contract is published in Pactflow)" do + before do + # A -> B + # Foo -> Bar + # A -> Bar + td.use_consumer("A") + .use_provider("Bar") + .create_integration + end + + subject do + Timecop.freeze(now) do + Repository.new.set_contract_data_updated_at(nil, td.and_return(:provider)) + end + end + + it "updates all the integrations for the provider" do + expect { subject }.to change { + Integration.select_all_qualified.filter_by_pacticipant("Bar").all.collect(&:contract_data_updated_at) + }.from([nil, nil]).to([now, now]) + end + end + end + end + end +end From 3b90aa64a175eed93621323cbc065293d852b437 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 15 Jun 2023 13:58:01 +1000 Subject: [PATCH 02/16] chore: add missing method --- lib/pact_broker/integrations/service.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pact_broker/integrations/service.rb b/lib/pact_broker/integrations/service.rb index b446df206..b50e1784f 100644 --- a/lib/pact_broker/integrations/service.rb +++ b/lib/pact_broker/integrations/service.rb @@ -30,6 +30,12 @@ def self.find_all .eager(:latest_verification) .all .sort { | a, b| Integration.compare_by_last_action_date(a, b) } + + # Callback to invoke when a consumer contract, verification result (or provider contract in Pactflow) is published + # @param [PactBroker::Domain::Pacticipant] consumer or nil + # @param [PactBroker::Domain::Pacticipant] provider + def self.handle_contract_data_published(consumer, provider) + integration_repository.set_contract_data_updated_at(consumer, provider) end def self.delete(consumer_name, provider_name) From 41cadfc8adf2a4041fe536477e726314c82c2ff5 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 15 Jun 2023 13:59:39 +1000 Subject: [PATCH 03/16] chore: commit missing end --- lib/pact_broker/integrations/service.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pact_broker/integrations/service.rb b/lib/pact_broker/integrations/service.rb index b50e1784f..3e172c9af 100644 --- a/lib/pact_broker/integrations/service.rb +++ b/lib/pact_broker/integrations/service.rb @@ -30,6 +30,7 @@ def self.find_all .eager(:latest_verification) .all .sort { | a, b| Integration.compare_by_last_action_date(a, b) } + end # Callback to invoke when a consumer contract, verification result (or provider contract in Pactflow) is published # @param [PactBroker::Domain::Pacticipant] consumer or nil From d02e2f3adaddf86d7a6f849b9e322426b713bc62 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 15 Jun 2023 14:03:59 +1000 Subject: [PATCH 04/16] chore: commit include --- lib/pact_broker/integrations/integration.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pact_broker/integrations/integration.rb b/lib/pact_broker/integrations/integration.rb index 93e46a7a2..7f07b3178 100644 --- a/lib/pact_broker/integrations/integration.rb +++ b/lib/pact_broker/integrations/integration.rb @@ -1,3 +1,4 @@ +require "pact_broker/repositories/helpers" require "pact_broker/verifications/pseudo_branch_status" require "pact_broker/domain/verification" require "pact_broker/webhooks/latest_triggered_webhook" @@ -75,6 +76,8 @@ class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :c end) dataset_module do + include PactBroker::Repositories::Helpers + def including_pacticipant_id(pacticipant_id) where(consumer_id: pacticipant_id).or(provider_id: pacticipant_id) end From 4ee694b6ea0c45ce8180f6a256901ee737c56d9b Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:31:05 +1000 Subject: [PATCH 05/16] chore: populate the new contract_data_updated_at field for existing integrations --- ...t_integrations_contract_data_updated_at.rb | 11 ++++ ...ntract_data_updated_at_for_integrations.rb | 47 +++++++++++++++++ lib/pact_broker/test/test_data_builder.rb | 4 ++ ...t_data_updated_at_for_integrations_spec.rb | 50 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 db/migrations/20230616_set_integrations_contract_data_updated_at.rb create mode 100644 lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb create mode 100644 spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb diff --git a/db/migrations/20230616_set_integrations_contract_data_updated_at.rb b/db/migrations/20230616_set_integrations_contract_data_updated_at.rb new file mode 100644 index 000000000..6f9c4ac9a --- /dev/null +++ b/db/migrations/20230616_set_integrations_contract_data_updated_at.rb @@ -0,0 +1,11 @@ +require "pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations" + +Sequel.migration do + up do + PactBroker::DB::DataMigrations::SetContractDataUpdatedAtForIntegrations.call(self) + end + + down do + + end +end diff --git a/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb b/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb new file mode 100644 index 000000000..498579303 --- /dev/null +++ b/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb @@ -0,0 +1,47 @@ +# Populate the newly created contract_data_updated_at date in the integrations table +# using the latest created_at date from the pact_publications or verifications tables. +module PactBroker + module DB + module DataMigrations + class SetContractDataUpdatedAtForIntegrations + def self.call(connection) + join = { + Sequel[:integrations][:consumer_id] => Sequel[:target][:consumer_id], + Sequel[:integrations][:provider_id] => Sequel[:target][:provider_id] + } + + max_created_at_for_each_integration = integrations_max_created_at(connection).from_self(alias: :target).select(:created_at).where(join) + + connection[:integrations] + .where(contract_data_updated_at: nil) + .update(contract_data_updated_at: max_created_at_for_each_integration) + end + + # @return [Sequel::Dataset] the overall max created_at from the union of the pact_publications and verifications tables, + # for each integration keyed by consumer_id/provider_id + def self.integrations_max_created_at(connection) + pact_publication_max_created_at(connection) + .union(verification_max_created_at(connection)) + .select_group(:consumer_id, :provider_id) + .select_append{ max(:created_at).as(:created_at) } + end + + # @return [Sequel::Dataset] the max created_at from the pact_publications table + # for each integration keyed by consumer_id/provider_id + def self.pact_publication_max_created_at(connection) + connection[:pact_publications] + .select_group(:consumer_id, :provider_id) + .select_append{ max(:created_at).as(:created_at) } + end + + # @return [Sequel::Dataset] the max created_at from the verifications table + # for each integration keyed by consumer_id/provider_id + def self.verification_max_created_at(connection) + connection[:verifications] + .select_group(:consumer_id, :provider_id) + .select_append{ max(:created_at).as(:created_at) } + end + end + end + end +end diff --git a/lib/pact_broker/test/test_data_builder.rb b/lib/pact_broker/test/test_data_builder.rb index 19a71fec0..43d6c9fb4 100644 --- a/lib/pact_broker/test/test_data_builder.rb +++ b/lib/pact_broker/test/test_data_builder.rb @@ -493,6 +493,10 @@ def and_return instance_variable_name instance_variable_get("@#{instance_variable_name}") end + def clear_now + @now = nil + end + def set_now date @now = date.to_date self diff --git a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb new file mode 100644 index 000000000..28b2330a3 --- /dev/null +++ b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb @@ -0,0 +1,50 @@ +require "pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations" +require "timecop" +require "tzinfo" + +module PactBroker + module DB + module DataMigrations + describe SetContractDataUpdatedAtForIntegrations do + before do + td.clear_now # use timecop instead of the TestDataBuilder @now + + Timecop.freeze(day_1) do + td.publish_pact(consumer_name: "Foo", provider_name: "Bar", consumer_version_number: "1") + end + + Timecop.freeze(day_2) do + td.publish_pact(consumer_name: "Foo", provider_name: "Bar", consumer_version_number: "2") + end + + Timecop.freeze(day_3) do + td.create_verification(provider_version: "2") + end + + Timecop.freeze(day_4) do + td.publish_pact(consumer_name: "Cat", provider_name: "Dog", consumer_version_number: "2") + end + + db[:integrations].update(contract_data_updated_at: nil) + end + + let(:day_1) { td.in_utc{ DateTime.new(2023, 6, 11) } } + let(:day_2) { td.in_utc{ DateTime.new(2023, 6, 12) } } + let(:day_3) { td.in_utc{ DateTime.new(2023, 6, 13) } } + let(:day_4) { td.in_utc{ DateTime.new(2023, 6, 14) } } + + let(:db) { PactBroker::Domain::Version.db } + + subject { SetContractDataUpdatedAtForIntegrations.call(db) } + + it "sets the contract_data_updated_at to the latest of the pact publication and verification publication dates for that integration" do + subject + integrations = db[:integrations].order(:id) + expect(integrations.first[:contract_data_updated_at]).to eq day_3 + expect(integrations.last[:contract_data_updated_at]).to eq day_4 + end + + end + end + end +end From 6bf7437a7b747e37fbdece7df52b6610decf99a0 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:35:19 +1000 Subject: [PATCH 06/16] test: update to use methods already committed --- spec/lib/pact_broker/integrations/repository_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/lib/pact_broker/integrations/repository_spec.rb b/spec/lib/pact_broker/integrations/repository_spec.rb index d24a0ea69..e61e00773 100644 --- a/spec/lib/pact_broker/integrations/repository_spec.rb +++ b/spec/lib/pact_broker/integrations/repository_spec.rb @@ -18,10 +18,12 @@ module Integrations let(:then) { Date.today - 20 } let(:now) { DateTime.new(2010, 11, 1, 1, 1, 1) } + let(:foo) { td.and_return(:consumer) } + let(:bar) { td.and_return(:provider) } subject do Timecop.freeze(now) do - Repository.new.set_contract_data_updated_at(td.and_return(:consumer), td.and_return(:provider)) + Repository.new.set_contract_data_updated_at(foo, bar) end end @@ -45,13 +47,13 @@ module Integrations subject do Timecop.freeze(now) do - Repository.new.set_contract_data_updated_at(nil, td.and_return(:provider)) + Repository.new.set_contract_data_updated_at(nil, bar) end end it "updates all the integrations for the provider" do expect { subject }.to change { - Integration.select_all_qualified.filter_by_pacticipant("Bar").all.collect(&:contract_data_updated_at) + Integration.select_all_qualified.including_pacticipant_id(bar.id).collect(&:contract_data_updated_at) }.from([nil, nil]).to([now, now]) end end From 4ca27e4e562492fc301a98629186956472a2abe1 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:42:45 +1000 Subject: [PATCH 07/16] refactor: move subscription into separate file --- lib/pact_broker/api.rb | 7 +------ lib/pact_broker/initializers/subscriptions.rb | 4 ++++ 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100644 lib/pact_broker/initializers/subscriptions.rb diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index 57a782fc8..299e3998d 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -5,6 +5,7 @@ require "pact_broker/api/contracts" require "pact_broker/application_context" require "pact_broker/feature_toggle" +require "pact_broker/initializers/subscriptions" module Webmachine class Request @@ -23,12 +24,6 @@ def really_put? end end -# Not sure where to put this - it's a global subscription -require "pact_broker/events/subscriber" -require "pact_broker/integrations/event_listener" - -PactBroker::Events.subscribe(PactBroker::Integrations::EventListener.new) - module PactBroker # rubocop: disable Metrics/MethodLength def self.build_api(application_context = PactBroker::ApplicationContext.default_application_context) diff --git a/lib/pact_broker/initializers/subscriptions.rb b/lib/pact_broker/initializers/subscriptions.rb new file mode 100644 index 000000000..7ac2bc32d --- /dev/null +++ b/lib/pact_broker/initializers/subscriptions.rb @@ -0,0 +1,4 @@ +require "pact_broker/events/subscriber" +require "pact_broker/integrations/event_listener" + +PactBroker::Events.subscribe(PactBroker::Integrations::EventListener.new) From 06ac9665f470973d511b2b10d5541041ff7cca2e Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:42:55 +1000 Subject: [PATCH 08/16] tests: include updated dataset module --- lib/pact_broker/integrations/integration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pact_broker/integrations/integration.rb b/lib/pact_broker/integrations/integration.rb index 7f07b3178..6f95c0b6a 100644 --- a/lib/pact_broker/integrations/integration.rb +++ b/lib/pact_broker/integrations/integration.rb @@ -1,4 +1,4 @@ -require "pact_broker/repositories/helpers" +require "pact_broker/dataset" require "pact_broker/verifications/pseudo_branch_status" require "pact_broker/domain/verification" require "pact_broker/webhooks/latest_triggered_webhook" @@ -76,7 +76,7 @@ class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :c end) dataset_module do - include PactBroker::Repositories::Helpers + include PactBroker::Dataset def including_pacticipant_id(pacticipant_id) where(consumer_id: pacticipant_id).or(provider_id: pacticipant_id) From ed0a0debfe9e9a84cb85fdf33450b71c08350e16 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:44:53 +1000 Subject: [PATCH 09/16] docs: comment --- lib/pact_broker/integrations/event_listener.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pact_broker/integrations/event_listener.rb b/lib/pact_broker/integrations/event_listener.rb index 8f002524e..200b4173c 100644 --- a/lib/pact_broker/integrations/event_listener.rb +++ b/lib/pact_broker/integrations/event_listener.rb @@ -1,5 +1,7 @@ require "pact_broker/services" +# Listens for events that happen in the Pact Broker that are relevant to the Integrations objects. + module PactBroker module Integrations class EventListener From d62a2148f86ae60f677a686a86effbbada2fcbff Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:50:15 +1000 Subject: [PATCH 10/16] docs: update --- lib/pact_broker/integrations/repository.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pact_broker/integrations/repository.rb b/lib/pact_broker/integrations/repository.rb index b5b1a99c6..ecac6853b 100644 --- a/lib/pact_broker/integrations/repository.rb +++ b/lib/pact_broker/integrations/repository.rb @@ -18,6 +18,9 @@ def delete(consumer_id, provider_id) Integration.where(consumer_id: consumer_id, provider_id: provider_id).delete end + # Sets the contract_data_updated_at for the integration(s) as specified by the consumer and provider + # @param [PactBroker::Domain::Pacticipant, nil] consumer the consumer for the integration, or nil if for a provider-only event (eg. Pactflow provider contract published) + # @param [PactBroker::Domain::Pacticipant] provider the provider for the integration def set_contract_data_updated_at(consumer, provider) Integration .where({ consumer_id: consumer&.id, provider_id: provider.id }.compact ) From 42e1f2f60988b521aed63896b9b4d04dc0a02223 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 11:52:16 +1000 Subject: [PATCH 11/16] style: whitespace --- .../set_contract_data_updated_at_for_integrations_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb index 28b2330a3..47a838157 100644 --- a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb +++ b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb @@ -43,7 +43,6 @@ module DataMigrations expect(integrations.first[:contract_data_updated_at]).to eq day_3 expect(integrations.last[:contract_data_updated_at]).to eq day_4 end - end end end From ed07cff24160b82b7e650604fde40097c95bb836 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 13:08:20 +1000 Subject: [PATCH 12/16] test: update for MySQL --- .../set_contract_data_updated_at_for_integrations_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb index 47a838157..95b6507a1 100644 --- a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb +++ b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb @@ -40,8 +40,8 @@ module DataMigrations it "sets the contract_data_updated_at to the latest of the pact publication and verification publication dates for that integration" do subject integrations = db[:integrations].order(:id) - expect(integrations.first[:contract_data_updated_at]).to eq day_3 - expect(integrations.last[:contract_data_updated_at]).to eq day_4 + expect(integrations.first[:contract_data_updated_at].to_s).to eq day_3.to_s # using to_s because dates are loaded as strings when running with MySQL + expect(integrations.last[:contract_data_updated_at].to_s).to eq day_4.to_s end end end From 791a03f48681f3ba3a8d3ced74f7718aa85aced0 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 13:09:58 +1000 Subject: [PATCH 13/16] chore: add data migration --- lib/pact_broker/db/migrate_data.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pact_broker/db/migrate_data.rb b/lib/pact_broker/db/migrate_data.rb index 42bfba3d6..94ac3cb79 100644 --- a/lib/pact_broker/db/migrate_data.rb +++ b/lib/pact_broker/db/migrate_data.rb @@ -29,6 +29,7 @@ def self.call database_connection, _options = {} DataMigrations::CreateBranches.call(database_connection) DataMigrations::MigrateIntegrations.call(database_connection) DataMigrations::MigratePactVersionProviderTagSuccessfulVerifications.call(database_connection) + DataMigrations::SetContractDataUpdatedAtForIntegrations.call(database_connection) end end end From d2a731242c1c5283f534314fa820062a17f3f5c9 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 13:27:23 +1000 Subject: [PATCH 14/16] test: use fancy date matching logic for mysql --- ...ontract_data_updated_at_for_integrations_spec.rb | 4 ++-- spec/support/rspec_matchers.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb index 95b6507a1..f0b153d46 100644 --- a/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb +++ b/spec/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations_spec.rb @@ -40,8 +40,8 @@ module DataMigrations it "sets the contract_data_updated_at to the latest of the pact publication and verification publication dates for that integration" do subject integrations = db[:integrations].order(:id) - expect(integrations.first[:contract_data_updated_at].to_s).to eq day_3.to_s # using to_s because dates are loaded as strings when running with MySQL - expect(integrations.last[:contract_data_updated_at].to_s).to eq day_4.to_s + expect(integrations.first[:contract_data_updated_at]).to be_date_time(day_3) + expect(integrations.last[:contract_data_updated_at]).to be_date_time(day_4) end end end diff --git a/spec/support/rspec_matchers.rb b/spec/support/rspec_matchers.rb index b821c9ae9..f976d23a9 100644 --- a/spec/support/rspec_matchers.rb +++ b/spec/support/rspec_matchers.rb @@ -1,3 +1,5 @@ +require "pact_broker/api/decorators/format_date_time" + RSpec::Matchers.define :be_datey do |_expected| match do |actual| actual.instance_of?(DateTime) || actual.instance_of?(Time) @@ -7,3 +9,14 @@ "expected #{actual.inspect} to be an instance of DateTime or Time" end end + +# Need this because dates get loaded into models as strings when using MySQL +RSpec::Matchers.define :be_date_time do |expected| + match do |actual| + PactBroker::Api::Decorators::FormatDateTime.call(expected) == PactBroker::Api::Decorators::FormatDateTime.call(actual) + end + + failure_message do |actual| + "expected #{PactBroker::Api::Decorators::FormatDateTime.call(expected)} to equal #{PactBroker::Api::Decorators::FormatDateTime.call(actual)}" + end +end From 209f8623e702541dda2ccd9aadd8693d1912662e Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 13:34:43 +1000 Subject: [PATCH 15/16] test: update for mysql --- spec/lib/pact_broker/integrations/repository_spec.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/lib/pact_broker/integrations/repository_spec.rb b/spec/lib/pact_broker/integrations/repository_spec.rb index e61e00773..f528bc237 100644 --- a/spec/lib/pact_broker/integrations/repository_spec.rb +++ b/spec/lib/pact_broker/integrations/repository_spec.rb @@ -52,9 +52,10 @@ module Integrations end it "updates all the integrations for the provider" do - expect { subject }.to change { - Integration.select_all_qualified.including_pacticipant_id(bar.id).collect(&:contract_data_updated_at) - }.from([nil, nil]).to([now, now]) + subject + integrations = Integration.select_all_qualified.including_pacticipant_id(bar.id) + expect(integrations.first.contract_data_updated_at).to be_date_time(now) + expect(integrations.last.contract_data_updated_at).to be_date_time(now) end end end From 175bad372467c4b982fa78d382cc63b66d1f52c5 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Fri, 16 Jun 2023 13:45:23 +1000 Subject: [PATCH 16/16] test: update for mysql --- spec/lib/pact_broker/integrations/repository_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/lib/pact_broker/integrations/repository_spec.rb b/spec/lib/pact_broker/integrations/repository_spec.rb index f528bc237..ff19b550f 100644 --- a/spec/lib/pact_broker/integrations/repository_spec.rb +++ b/spec/lib/pact_broker/integrations/repository_spec.rb @@ -28,7 +28,8 @@ module Integrations end it "updates the contract_data_updated_at to now" do - expect { subject }.to change { Integration.last.contract_data_updated_at }.from(nil).to(now) + subject + expect(Integration.last.contract_data_updated_at).to be_date_time(now) end it "does not update the other integrations" do