From 95e249f135702c502ac8c0edc7f437337458796b Mon Sep 17 00:00:00 2001 From: Guilherme Souza Date: Tue, 24 Sep 2024 10:54:46 -0300 Subject: [PATCH] fix(realtime): add missing `onPostgresChange` overload (#528) --- Package.swift | 2 + Sources/Realtime/PhoenixTransport.swift | 4 +- Sources/Realtime/V2/RealtimeChannelV2.swift | 20 +++- .../RealtimeTests/RealtimeChannelTests.swift | 102 ++++++++++++++++++ 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 Tests/RealtimeTests/RealtimeChannelTests.swift diff --git a/Package.swift b/Package.swift index bff948f7..95e9b7d6 100644 --- a/Package.swift +++ b/Package.swift @@ -120,6 +120,8 @@ let package = Package( name: "RealtimeTests", dependencies: [ .product(name: "CustomDump", package: "swift-custom-dump"), + .product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"), + .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), "PostgREST", "Realtime", "TestHelpers", diff --git a/Sources/Realtime/PhoenixTransport.swift b/Sources/Realtime/PhoenixTransport.swift index 53d7965f..79c85400 100644 --- a/Sources/Realtime/PhoenixTransport.swift +++ b/Sources/Realtime/PhoenixTransport.swift @@ -200,8 +200,8 @@ open class URLSessionTransport: NSObject, PhoenixTransport, URLSessionWebSocketD session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil) var request = URLRequest(url: url) - headers.forEach { (key: String, value: Any) in - guard let value = value as? String else { return } + for (key, value) in headers { + guard let value = value as? String else { continue } request.addValue(value, forHTTPHeaderField: key) } diff --git a/Sources/Realtime/V2/RealtimeChannelV2.swift b/Sources/Realtime/V2/RealtimeChannelV2.swift index d9714bf2..a3ab7235 100644 --- a/Sources/Realtime/V2/RealtimeChannelV2.swift +++ b/Sources/Realtime/V2/RealtimeChannelV2.swift @@ -86,7 +86,7 @@ public final class RealtimeChannelV2: Sendable { let logger: (any SupabaseLogger)? let socket: Socket - private let callbackManager = CallbackManager() + let callbackManager = CallbackManager() private let statusEventEmitter = EventEmitter(initialEvent: .unsubscribed) public private(set) var status: Status { @@ -472,6 +472,24 @@ public final class RealtimeChannelV2: Sendable { } } + /// Listen for postgres changes in a channel. + public func onPostgresChange( + _: AnyAction.Type, + schema: String = "public", + table: String? = nil, + filter: String? = nil, + callback: @escaping @Sendable (AnyAction) -> Void + ) -> Subscription { + _onPostgresChange( + event: .all, + schema: schema, + table: table, + filter: filter + ) { + callback($0) + } + } + /// Listen for postgres changes in a channel. public func onPostgresChange( _: InsertAction.Type, diff --git a/Tests/RealtimeTests/RealtimeChannelTests.swift b/Tests/RealtimeTests/RealtimeChannelTests.swift new file mode 100644 index 00000000..baad4746 --- /dev/null +++ b/Tests/RealtimeTests/RealtimeChannelTests.swift @@ -0,0 +1,102 @@ +// +// RealtimeChannelTests.swift +// Supabase +// +// Created by Guilherme Souza on 09/09/24. +// + +import InlineSnapshotTesting +@testable import Realtime +import XCTest +import XCTestDynamicOverlay + +final class RealtimeChannelTests: XCTestCase { + var sut: RealtimeChannelV2! + + func testOnPostgresChange() { + sut = RealtimeChannelV2( + topic: "topic", + config: RealtimeChannelConfig( + broadcast: BroadcastJoinConfig(), + presence: PresenceJoinConfig(), + isPrivate: false + ), + socket: .mock, + logger: nil + ) + var subscriptions = Set() + sut.onPostgresChange(AnyAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(InsertAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(UpdateAction.self) { _ in }.store(in: &subscriptions) + sut.onPostgresChange(DeleteAction.self) { _ in }.store(in: &subscriptions) + + assertInlineSnapshot(of: sut.callbackManager.callbacks, as: .dump) { + """ + ▿ 4 elements + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.all + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 1 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.insert + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 2 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.update + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 3 + ▿ RealtimeCallback + ▿ postgres: PostgresCallback + - callback: (Function) + ▿ filter: PostgresJoinConfig + ▿ event: Optional + - some: PostgresChangeEvent.delete + - filter: Optional.none + - id: 0 + - schema: "public" + - table: Optional.none + - id: 4 + + """ + } + } +} + +extension Socket { + static var mock: Socket { + Socket( + broadcastURL: unimplemented(), + status: unimplemented(), + options: unimplemented(), + accessToken: unimplemented(), + apiKey: unimplemented(), + makeRef: unimplemented(), + connect: unimplemented(), + addChannel: unimplemented(), + removeChannel: unimplemented(), + push: unimplemented(), + httpSend: unimplemented() + ) + } +}