From 3dc2dc5450cb4bfbbd278787109d1c6daaea19c1 Mon Sep 17 00:00:00 2001 From: tr00d Date: Thu, 6 Aug 2026 15:21:35 +0200 Subject: [PATCH] refactor: deprecate two-step postgres_changes API for OnPostgresChange Mark Register(PostgresChangesOptions) and AddPostgresChangeHandler [Obsolete], pointing at OnPostgresChange, which registers the option and binds the handler in a single call. Route Client's internal registration through a non-obsolete seam, and move in-repo callers (tests, Examples) onto OnPostgresChange so the tree stays warning-clean. --- Realtime/Client.cs | 2 +- Realtime/Interfaces/IRealtimeChannel.cs | 5 +- Realtime/RealtimeChannel.cs | 4 +- RealtimeTests/ChannelPostgresChangesTests.cs | 107 +++++++++---------- 4 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Realtime/Client.cs b/Realtime/Client.cs index ad6b1de..86e90d1 100644 --- a/Realtime/Client.cs +++ b/Realtime/Client.cs @@ -404,7 +404,7 @@ public RealtimeChannel Channel(string database = "realtime", string schema = "pu var options = ChannelOptions.Public(Options, () => AccessToken, SerializerSettings); var subscription = new RealtimeChannel(Socket!, key, options); - subscription.Register(changesOptions); + subscription.RegisterPostgresChangesOptions(changesOptions); _subscriptions.Add(key, subscription); diff --git a/Realtime/Interfaces/IRealtimeChannel.cs b/Realtime/Interfaces/IRealtimeChannel.cs index 277702e..5dc75cb 100644 --- a/Realtime/Interfaces/IRealtimeChannel.cs +++ b/Realtime/Interfaces/IRealtimeChannel.cs @@ -1,4 +1,5 @@ -using Supabase.Realtime.Broadcast; +using System; +using Supabase.Realtime.Broadcast; using Supabase.Realtime.Channel; using Supabase.Realtime.Models; using Supabase.Realtime.PostgresChanges; @@ -162,6 +163,7 @@ IRealtimeChannel OnPostgresChange(PostgresChangesHandler postgresChangeHandler, /// /// /// + [Obsolete("Favor OnPostgresChange instead.")] void AddPostgresChangeHandler(ListenType listenType, PostgresChangesHandler postgresChangeHandler); /// @@ -266,6 +268,7 @@ RealtimePresence Register(string presenceK /// /// /// + [Obsolete("Favor OnPostgresChange instead.")] IRealtimeChannel Register(PostgresChangesOptions postgresChangesOptions); /// diff --git a/Realtime/RealtimeChannel.cs b/Realtime/RealtimeChannel.cs index ce8cc84..fd032f5 100644 --- a/Realtime/RealtimeChannel.cs +++ b/Realtime/RealtimeChannel.cs @@ -352,6 +352,7 @@ public IRealtimeChannel OnPostgresChange(PostgresChangesHandler postgresChangeHa /// /// The type of event this callback should process. /// + [Obsolete("Favor OnPostgresChange instead.")] public void AddPostgresChangeHandler(ListenType listenType, PostgresChangesHandler postgresChangeHandler) { BindPostgresChangesHandler(listenType, postgresChangeHandler); @@ -437,6 +438,7 @@ private void NotifyPostgresChanges(EventType eventType, PostgresChangesResponse /// /// /// + [Obsolete("Favor OnPostgresChange instead.")] public IRealtimeChannel Register(PostgresChangesOptions postgresChangesOptions) { RegisterPostgresChangesOptions(postgresChangesOptions); @@ -448,7 +450,7 @@ public IRealtimeChannel Register(PostgresChangesOptions postgresChangesOptions) /// and share a single registration path. /// /// - private void RegisterPostgresChangesOptions(PostgresChangesOptions postgresChangesOptions) + internal void RegisterPostgresChangesOptions(PostgresChangesOptions postgresChangesOptions) { PostgresChangesOptions.Add(postgresChangesOptions); BindPostgresChangesOptions(postgresChangesOptions); diff --git a/RealtimeTests/ChannelPostgresChangesTests.cs b/RealtimeTests/ChannelPostgresChangesTests.cs index d76cf91..b889a30 100644 --- a/RealtimeTests/ChannelPostgresChangesTests.cs +++ b/RealtimeTests/ChannelPostgresChangesTests.cs @@ -34,18 +34,17 @@ public void CleanupTest() _socketClient!.Disconnect(); } - [TestMethod("Channel: Payload returns a modeled response (if possible)")] + [TestMethod(DisplayName = "Channel: Payload returns a modeled response (if possible)")] public async Task ChannelPayloadReturnsModel() { var tsc = new TaskCompletionSource(); var channel = _socketClient!.Channel("example"); - channel.Register(new PostgresChangesOptions("public", "*")); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, changes) => + channel.OnPostgresChange((_, changes) => { var model = changes.Model(); tsc.SetResult(model != null); - }); + }, ListenType.Inserts, new PostgresChangesFilter { Table = "*" }); await channel.Subscribe(); @@ -55,14 +54,15 @@ public async Task ChannelPayloadReturnsModel() Assert.IsTrue(check); } - [TestMethod("Channel: Receives Insert Callback")] + [TestMethod(DisplayName = "Channel: Receives Insert Callback")] public async Task ChannelReceivesInsertCallback() { var tsc = new TaskCompletionSource(); var channel = _socketClient!.Channel("realtime", "public", "todos"); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, _) => tsc.SetResult(true)); + channel.OnPostgresChange((_, _) => tsc.SetResult(true), ListenType.Inserts, + new PostgresChangesFilter { Table = "todos" }); await channel.Subscribe(); await _restClient!.Table() @@ -72,22 +72,22 @@ public async Task ChannelReceivesInsertCallback() Assert.IsTrue(check); } - [TestMethod("Channel: Receives Filtered Insert Callback")] + [TestMethod(DisplayName = "Channel: Receives Filtered Insert Callback")] public async Task ChannelReceivesInsertCallbackFiltered() { var tsc = new TaskCompletionSource(); - var channel = _socketClient!.Channel("realtime", "public", "todos", "details", - "Client receives filtered insert callback? ✅"); - - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, changes) => + var channel = _socketClient!.Channel("realtime", "public", "todos"); + + channel.OnPostgresChange((_, changes) => { var oldModel = changes.Model(); Assert.AreEqual("Client receives filtered insert callback? ✅", oldModel?.Details); tsc.SetResult(true); - }); + }, ListenType.Inserts, + new PostgresChangesFilter { Table = "todos", Filter = "details=eq.Client receives filtered insert callback? ✅" }); await channel.Subscribe(); await _restClient!.Table() @@ -95,12 +95,12 @@ public async Task ChannelReceivesInsertCallbackFiltered() await _restClient!.Table() .Insert(new Todo { UserId = 2, Details = "Client receives filtered insert callback? ✅" }); - + var check = await tsc.Task; Assert.IsTrue(check); } - [TestMethod("Channel: Receives Filtered Two Callback")] + [TestMethod(DisplayName = "Channel: Receives Filtered Two Callback")] public async Task ChannelReceivesTwoCallbacks() { var tsc = new TaskCompletionSource(); @@ -115,7 +115,7 @@ public async Task ChannelReceivesTwoCallbacks() var newDetails = $"I'm an updated item ✏️ - {DateTime.Now}"; var channel = _socketClient!.Channel("realtime", "public", "todos"); - channel.AddPostgresChangeHandler(ListenType.Updates, (_, changes) => + channel.OnPostgresChange((_, changes) => { var oldModel = changes.OldModel(); @@ -131,19 +131,18 @@ public async Task ChannelReceivesTwoCallbacks() } tsc.SetResult(true); - }); - + }, ListenType.Updates, new PostgresChangesFilter { Table = "todos" }); + const string filter = "Client receives filtered insert callback? ✅"; - channel.Register(new PostgresChangesOptions("public", "todos", ListenType.Inserts, $"details=eq.{filter}")); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, changes) => + channel.OnPostgresChange((_, changes) => { var insertedModel = changes.Model(); Assert.AreEqual("Client receives filtered insert callback? ✅", insertedModel?.Details); tsc.SetResult(true); - }); - + }, ListenType.Inserts, new PostgresChangesFilter { Table = "todos", Filter = $"details=eq.{filter}" }); + await channel.Subscribe(); await _restClient.Table() @@ -154,8 +153,8 @@ await _restClient.Table() var check = await tsc.Task; Assert.IsTrue(check); } - - [TestMethod("Channel: Receives Update Callback")] + + [TestMethod(DisplayName = "Channel: Receives Update Callback")] public async Task ChannelReceivesUpdateCallback() { var tsc = new TaskCompletionSource(); @@ -169,7 +168,7 @@ public async Task ChannelReceivesUpdateCallback() var channel = _socketClient!.Channel("realtime", "public", "todos"); - channel.AddPostgresChangeHandler(ListenType.Updates, (_, changes) => + channel.OnPostgresChange((_, changes) => { var oldModel = changes.OldModel(); @@ -185,7 +184,7 @@ public async Task ChannelReceivesUpdateCallback() } tsc.SetResult(true); - }); + }, ListenType.Updates, new PostgresChangesFilter { Table = "todos" }); await channel.Subscribe(); @@ -198,14 +197,15 @@ await _restClient.Table() Assert.IsTrue(check); } - [TestMethod("Channel: Receives Delete Callback")] + [TestMethod(DisplayName = "Channel: Receives Delete Callback")] public async Task ChannelReceivesDeleteCallback() { var tsc = new TaskCompletionSource(); var channel = _socketClient!.Channel("realtime", "public", "todos"); - channel.AddPostgresChangeHandler(ListenType.Deletes, (_, _) => tsc.SetResult(true)); + channel.OnPostgresChange((_, _) => tsc.SetResult(true), ListenType.Deletes, + new PostgresChangesFilter { Table = "todos" }); await channel.Subscribe(); @@ -218,28 +218,28 @@ public async Task ChannelReceivesDeleteCallback() Assert.IsTrue(check); } - [TestMethod("Channel: Receives Delete Callback")] + [TestMethod(DisplayName = "Channel: Receives Delete Callback")] public async Task ChannelReceivesFilteredDeleteCallback() { var tsc = new TaskCompletionSource(); var channel = _socketClient!.Channel("realtime", "public", "todos"); - + var todo1 = await _restClient!.Table().Insert(new Todo { UserId = 1, Details = "Client receives callbacks 1? ✅" }); var todo2 = await _restClient!.Table().Insert(new Todo { UserId = 2, Details = "Client receives callbacks 2? ✅" }); await _restClient!.Table().Insert(new Todo { UserId = 3, Details = "Client receives callbacks 3? ✅" }); - - channel.Register(new PostgresChangesOptions("public", "todos", ListenType.Deletes, $"details=eq.{todo1.Model?.Details}")); - channel.AddPostgresChangeHandler(ListenType.Deletes, (_, removed) => + + channel.OnPostgresChange((_, removed) => { - var result = removed.OldModel(); + var result = removed.OldModel(); Assert.AreEqual(result?.Details, todo1.Model?.Details); Assert.AreNotEqual(result?.Details, todo2.Model?.Details); - + tsc.SetResult(true); - }); + }, ListenType.Deletes, + new PostgresChangesFilter { Table = "todos", Filter = $"details=eq.{todo1.Model?.Details}" }); await channel.Subscribe(); @@ -249,8 +249,8 @@ public async Task ChannelReceivesFilteredDeleteCallback() var check = await tsc.Task; Assert.IsTrue(check); } - - [TestMethod("Channel: Receives '*' Callback")] + + [TestMethod(DisplayName = "Channel: Receives '*' Callback")] public async Task ChannelReceivesWildcardCallback() { var insertTsc = new TaskCompletionSource(); @@ -261,7 +261,7 @@ public async Task ChannelReceivesWildcardCallback() var channel = _socketClient!.Channel("realtime", "public", "todos"); - channel.AddPostgresChangeHandler(ListenType.All, (_, changes) => + channel.OnPostgresChange((_, changes) => { switch (changes.Payload?.Data?.Type) { @@ -275,7 +275,7 @@ public async Task ChannelReceivesWildcardCallback() deleteTsc.SetResult(true); break; } - }); + }, ListenType.All, new PostgresChangesFilter { Table = "todos" }); await channel.Subscribe(); @@ -293,7 +293,7 @@ public async Task ChannelReceivesWildcardCallback() Assert.IsTrue(deleteTsc.Task.Result); } - [TestMethod("Channel: Receives Several Same Callback")] + [TestMethod(DisplayName = "Channel: Receives Several Same Callback")] public async Task ChannelReceivesSeveralSameCallback() { var insertTask1 = new TaskCompletionSource(); @@ -301,41 +301,36 @@ public async Task ChannelReceivesSeveralSameCallback() var insertTask3 = new TaskCompletionSource(); const string filter1 = "Client receives callbacks 1? ✅"; const string filter2 = "Client receives callbacks 2? ✅"; - + var channel = _socketClient!.Channel("realtime", "public", "todos"); var count = 0; - channel.Register(new PostgresChangesOptions("public", "todos", ListenType.Inserts)); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, added) => + channel.OnPostgresChange((_, added) => { count++; if (count == 3) insertTask1.TrySetResult(true); - }); + }, ListenType.Inserts, new PostgresChangesFilter { Table = "todos" }); - channel.Register(new PostgresChangesOptions("public", "todos", ListenType.Inserts, $"details=eq.{filter1}")); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, added) => + channel.OnPostgresChange((_, added) => { var model = added.Model(); - - insertTask2.SetResult(model?.Details == filter1); - }); + insertTask2.SetResult(model?.Details == filter1); + }, ListenType.Inserts, new PostgresChangesFilter { Table = "todos", Filter = $"details=eq.{filter1}" }); - channel.Register(new PostgresChangesOptions("public", "todos", ListenType.Inserts, $"details=eq.{filter2}")); - channel.AddPostgresChangeHandler(ListenType.Inserts, (_, added) => + channel.OnPostgresChange((_, added) => { var model = added.Model(); insertTask3.SetResult(model?.Details == filter2); - }); - + }, ListenType.Inserts, new PostgresChangesFilter { Table = "todos", Filter = $"details=eq.{filter2}" }); await channel.Subscribe(); - + await _restClient!.Table().Insert(new Todo { UserId = 1, Details = "Client receives wildcard callbacks? ✅" }); await _restClient!.Table().Insert(new Todo { UserId = 1, Details = filter1 }); await _restClient!.Table().Insert(new Todo { UserId = 1, Details = filter2 }); - + await Task.WhenAll(insertTask1.Task, insertTask2.Task, insertTask3.Task); Assert.IsTrue(insertTask1.Task.Result); @@ -366,4 +361,4 @@ private static async Task WithinTimeout(Task task, int timeoutMs = 1 return completed == task && task.Result; } -} \ No newline at end of file +}