diff --git a/db/migrate/20241105135438_add_notify_trigger_for_route_changes.rb b/db/migrate/20241105135438_add_notify_trigger_for_route_changes.rb new file mode 100644 index 00000000..96173368 --- /dev/null +++ b/db/migrate/20241105135438_add_notify_trigger_for_route_changes.rb @@ -0,0 +1,38 @@ +class AddNotifyTriggerForRouteChanges < ActiveRecord::Migration[7.2] + def up + execute <<-SQL + CREATE OR REPLACE FUNCTION notify_route_change() RETURNS trigger AS $$ + BEGIN + PERFORM pg_notify('route_changes', ''); + RETURN OLD; + END; + $$ LANGUAGE plpgsql; + SQL + + execute <<-SQL + CREATE TRIGGER content_item_change_trigger + AFTER INSERT OR UPDATE OR DELETE ON content_items + FOR EACH ROW EXECUTE PROCEDURE notify_route_change(); + SQL + + execute <<-SQL + CREATE TRIGGER publish_intent_change_trigger + AFTER INSERT OR UPDATE OR DELETE ON publish_intents + FOR EACH ROW EXECUTE PROCEDURE notify_route_change(); + SQL + end + + def down + execute <<-SQL + DROP TRIGGER IF EXISTS content_item_change_trigger ON content_items; + SQL + + execute <<-SQL + DROP TRIGGER IF EXISTS publish_intent_change_trigger ON publish_intents; + SQL + + execute <<-SQL + DROP FUNCTION IF EXISTS notify_route_change(); + SQL + end +end diff --git a/db/schema.rb b/db/schema.rb index 3e245896..a5b096ec 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_03_12_132747) do +ActiveRecord::Schema[7.2].define(version: 2024_11_05_135438) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -114,5 +114,4 @@ t.index ["uid"], name: "index_users_on_uid", unique: true t.index ["updated_at"], name: "index_users_on_updated_at" end - end diff --git a/spec/integration/notify_route_change_spec.rb b/spec/integration/notify_route_change_spec.rb new file mode 100644 index 00000000..ecf11c04 --- /dev/null +++ b/spec/integration/notify_route_change_spec.rb @@ -0,0 +1,77 @@ +require "rails_helper" + +def postgres_listener(channel) + queue = Queue.new + + Thread.new do + conn = ActiveRecord::Base.lease_connection + conn.execute("LISTEN #{channel}") + loop do + conn.raw_connection.wait_for_notify do |event, _id, _data| + queue << event + end + end + ensure + conn.execute "UNLISTEN #{channel}" + end + + # Wait for the thread to be ready to listen for nofitications + sleep 0.1 + + queue +end + +describe "Postgres trigger", type: :request, skip_db_cleaner: true do + before do + # We need to use truncation instead of transaction to ensure the changes are commited and the trigger is fired + DatabaseCleaner.clean + DatabaseCleaner.strategy = :truncation + end + + it "sends a notification when content item created" do + listener = postgres_listener("route_changes") + create(:content_item) + + expect(listener.pop).to eq("route_changes") + end + + it "sends a notification when content item updated" do + content_item = create(:content_item) + + listener = postgres_listener("route_changes") + content_item.update!(base_path: "/foo") + + expect(listener.pop).to eq("route_changes") + end + + it "sends a notification when content item destroyed" do + content_item = create(:content_item) + listener = postgres_listener("route_changes") + content_item.destroy! + + expect(listener.pop).to eq("route_changes") + end + + it "sends a notification when publish intent created" do + listener = postgres_listener("route_changes") + create(:publish_intent) + + expect(listener.pop).to eq("route_changes") + end + + it "sends a notification when publish intent updated" do + publish_intent = create(:publish_intent) + listener = postgres_listener("route_changes") + publish_intent.update!(publish_time: 10.minutes.from_now) + + expect(listener.pop).to eq("route_changes") + end + + it "sends a notification when publish intent destroyed" do + publish_intent = create(:publish_intent) + listener = postgres_listener("route_changes") + publish_intent.destroy! + + expect(listener.pop).to eq("route_changes") + end +end