-
-
Notifications
You must be signed in to change notification settings - Fork 69
Notify listen #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Notify listen #54
Changes from all commits
86e1d80
7a13eb7
910c51f
4435811
54e9e04
2b20d60
fb82850
4a4b7ac
fe5774d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,15 @@ public final class PostgreSQLConnection: DatabaseConnection, BasicWorker { | |
| /// The current query running, if one exists. | ||
| private var pipeline: Future<Void> | ||
|
|
||
| /// Block type to be called on close of connection | ||
| internal typealias CloseHandler = ((PostgreSQLConnection) -> Future<Void>) | ||
| /// Called on close of the connection | ||
| internal var closeHandlers = [CloseHandler]() | ||
| /// Handler type for Notifications | ||
| internal typealias NotificationHandler = (String) throws -> Void | ||
| /// Handlers to be stored by channel name | ||
| internal var notificationHandlers: [String: NotificationHandler] = [:] | ||
|
|
||
| /// Creates a new Redis client on the provided data source and sink. | ||
| init(queue: QueueHandler<PostgreSQLMessage, PostgreSQLMessage>, channel: Channel) { | ||
| self.queue = queue | ||
|
|
@@ -184,19 +193,24 @@ public final class PostgreSQLConnection: DatabaseConnection, BasicWorker { | |
| } | ||
| } | ||
|
|
||
| internal var beforeClose: ((PostgreSQLConnection) -> Future<Void>)? | ||
|
|
||
| /// Closes this client. | ||
| public func close() { | ||
| if let beforeClose = beforeClose { | ||
| _ = beforeClose(self).then { _ in | ||
| self.channel.close(mode: CloseMode.all) | ||
| _ = executeCloseHandlersThenClose() | ||
| } | ||
|
|
||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not: extra newline and two spaces before „func“ |
||
| private func executeCloseHandlersThenClose() -> Future<Void> { | ||
| if let beforeClose = closeHandlers.popLast() { | ||
| return beforeClose(self).then { _ in | ||
| self.executeCloseHandlersThenClose() | ||
| } | ||
| } else { | ||
| channel.close(promise: nil) | ||
| return channel.close(mode: .all) | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /// Called when this class deinitializes. | ||
| deinit { | ||
| close() | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,12 @@ import Foundation | |
|
|
||
| struct PostgreSQLNotificationResponse: Decodable { | ||
| /// The message coming from PSQL | ||
| let channel: String | ||
| let message: String | ||
| init(from decoder: Decoder) throws { | ||
| let container = try decoder.singleValueContainer() | ||
| _ = try container.decode(Int32.self) // message length | ||
| _ = try container.decode(Int32.self) // process id of message | ||
| let channelId = try container.decode(String.self) | ||
| let message = try? container.decode(String.self) | ||
| self.message = message ?? channelId | ||
| _ = try container.decode(Int32.self) | ||
| channel = try container.decode(String.self) | ||
| message = try container.decode(String.self) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Postgres allows for now message to be provided, so I think message should be an optional and this an optional decode? Or will the message simply be empty in that case? (At least worth testing.) |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -348,7 +348,7 @@ class PostgreSQLConnectionTests: XCTestCase { | |
| let completionHandlerExpectation2 = expectation(description: "final completion handler called") | ||
| let notifyConn = try PostgreSQLConnection.makeTest() | ||
| let listenConn = try PostgreSQLConnection.makeTest() | ||
| let channelName = "Foo" | ||
| let channelName = "Fooze" | ||
| let messageText = "Bar" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: also test with a message text longer than 3 characters, and test with an empty message? (See above.) |
||
| let finalMessageText = "Baz" | ||
|
|
||
|
|
@@ -363,11 +363,65 @@ class PostgreSQLConnectionTests: XCTestCase { | |
| try notifyConn.notify(channelName, message: messageText).wait() | ||
| try notifyConn.notify(channelName, message: finalMessageText).wait() | ||
|
|
||
| waitForExpectations(timeout: defaultTimeout) | ||
| notifyConn.close() | ||
| listenConn.close() | ||
| } | ||
|
|
||
| func testNotifyAndListenOnMultipleChannels() throws { | ||
| let completionHandlerExpectation1 = expectation(description: "first completion handler called") | ||
| let completionHandlerExpectation2 = expectation(description: "final completion handler called") | ||
| let notifyConn = try PostgreSQLConnection.makeTest() | ||
| let listenConn = try PostgreSQLConnection.makeTest() | ||
| let channelName = "Fooze" | ||
| let channelName2 = "Foozalz" | ||
| let messageText = "Bar" | ||
| let finalMessageText = "Baz" | ||
|
|
||
| try listenConn.listen(channelName) { text in | ||
| if text == messageText { | ||
| completionHandlerExpectation1.fulfill() | ||
| } | ||
| }.catch({ err in XCTFail("error \(err)") }) | ||
|
|
||
| try listenConn.listen(channelName2) { text in | ||
| if text == finalMessageText { | ||
| completionHandlerExpectation2.fulfill() | ||
| } | ||
| }.catch({ err in XCTFail("error \(err)") }) | ||
|
|
||
| try notifyConn.notify(channelName, message: messageText).wait() | ||
| try notifyConn.notify(channelName2, message: finalMessageText).wait() | ||
|
|
||
| waitForExpectations(timeout: defaultTimeout) | ||
| notifyConn.close() | ||
| listenConn.close() | ||
| } | ||
|
|
||
| func testUnlisten() throws { | ||
| let unlistenHandlerExpectation = expectation(description: "unlisten completion handler called") | ||
|
|
||
| let listenHandlerExpectation = expectation(description: "listen completion handler called") | ||
|
|
||
| let notifyConn = try PostgreSQLConnection.makeTest() | ||
| let listenConn = try PostgreSQLConnection.makeTest() | ||
| let channelName = "Foozers" | ||
| let messageText = "Bar" | ||
|
|
||
| try listenConn.listen(channelName) { text in | ||
| if text == messageText { | ||
| listenHandlerExpectation.fulfill() | ||
| } | ||
| }.catch({ err in XCTFail("error \(err)") }) | ||
|
|
||
| try notifyConn.notify(channelName, message: messageText).wait() | ||
| try notifyConn.unlisten(channelName, unlistenHandler: { | ||
| unlistenHandlerExpectation.fulfill() | ||
| }).wait() | ||
| waitForExpectations(timeout: defaultTimeout) | ||
| notifyConn.close() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also test that no further notifications are sent after |
||
| listenConn.close() | ||
| } | ||
|
|
||
| func testURLParsing() throws { | ||
| let databaseURL = "postgres://username:password@hostname.com:5432/database" | ||
|
|
@@ -389,6 +443,8 @@ class PostgreSQLConnectionTests: XCTestCase { | |
| ("testNull", testNull), | ||
| ("testGH24", testGH24), | ||
| ("testNotifyAndListen", testNotifyAndListen), | ||
| ("testNotifyAndListenOnMultipleChannels", testNotifyAndListenOnMultipleChannels), | ||
| ("testUnlisten", testUnlisten), | ||
| ("testURLParsing", testURLParsing), | ||
| ] | ||
| } | ||
|
|
@@ -398,8 +454,7 @@ extension PostgreSQLConnection { | |
| static func makeTest() throws -> PostgreSQLConnection { | ||
| let hostname: String | ||
| #if Xcode | ||
| //hostname = (try? Process.execute("docker-machine", "ip")) ?? "192.168.99.100" | ||
| hostname = "localhost" | ||
| hostname = (try? Process.execute("docker-machine", "ip")) ?? "192.168.99.100" | ||
| #else | ||
| hostname = "localhost" | ||
| #endif | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Postgres‘ notify command allows omitting the payload, so how about allowing
Nilmessages innotify?