Skip to content

Conversation

@ttypic
Copy link
Contributor

@ttypic ttypic commented Jan 8, 2026

  • Introduced publishWithResult API to include message serials in the response.
  • Added callback-based publishAsync method for asynchronous publishing with serials.
  • Deprecated older CompletionListener-based publish methods in favor of Callback<PublishResult>.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added new publish methods returning PublishResult with message metadata and serials.
    • Added asynchronous publish variants with typed result callbacks for better error handling and result access.
  • Deprecations

    • Deprecated CompletionListener-based publish methods; use result-based variants instead.
  • Improvements

    • Enhanced type safety in asynchronous callback handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Walkthrough

The pull request introduces a new PublishResult type to capture event publication operation results. New callback-based publish APIs are added to realtime and REST channels, while existing CompletionListener-based variants are deprecated. The generic ToCallback<T> helper is updated to support typed results, and tests are refactored to verify the new result-driven publish flow.

Changes

Cohort / File(s) Change Summary
Generic Callback Infrastructure
lib/src/main/java/io/ably/lib/realtime/CompletionListener.java, android/src/main/java/io/ably/lib/push/PushChannel.java, lib/src/main/java/io/ably/lib/push/PushBase.java
Made ToCallback generic with type parameter <T> to support typed callback results; updated call sites to use ToCallback<>(listener) with explicit generic syntax.
Realtime Channel API
lib/src/main/java/io/ably/lib/realtime/ChannelBase.java
Added three new overloads of publish() accepting Callback<PublishResult> for single and multiple message variants; deprecated corresponding CompletionListener-based methods; existing methods now forward to callback-based implementations with null callback.
REST Channel API
lib/src/main/java/io/ably/lib/rest/ChannelBase.java
Introduced publishWithResult() methods returning PublishResult for single and multiple messages; added publishAsync() overloads accepting Callback<PublishResult>; updated internal publishImpl() signatures to return Http.Request<PublishResult> instead of Http.Request<Void>.
Realtime Test Updates
lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelMessageEditTest.java, lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelTest.java
Refactored tests to use PublishResult obtained from publish operations; replaced history-based serial retrieval with PublishResult.serials[0]; removed explicit null CompletionListener arguments in publish calls.
REST Test Updates
lib/src/test/java/io/ably/lib/test/rest/RestChannelMessageEditTest.java
Updated all publish calls to use publishWithResult() and extract serials via PublishResult.serials[0]; added exception handling for 404 responses during polling; adjusted assertion logic to rely on publish results.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


🐰 Hopping through callbacks with glee,
PublishResult sets the data free,
No more void returns in the night,
Type-safe results shine oh so bright,
Generic rabbits hop left, then right! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The linked issue (AIT-84) title does not match the PR objectives; the issue title mentions 'Token streaming with compact history' rather than 'message publish results'. Verify that AIT-84 is the correct issue or confirm the actual issue(s) this PR addresses to validate alignment.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main feature: adding support for returning serials on publish operations.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the publish result feature: generic type updates, new PublishResult APIs, callback-based methods, and deprecations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch AIT-84/message-publish-result

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot temporarily deployed to staging/pull/1184/features January 8, 2026 23:52 Inactive
@ttypic ttypic force-pushed the AIT-98/message-edits-deletes branch 8 times, most recently from c4ffcd4 to 99a2c94 Compare January 13, 2026 10:48
- Introduced `publishWithResult` API to include message serials in the response.
- Added callback-based `publishAsync` method for asynchronous publishing with serials.
- Deprecated older `CompletionListener`-based publish methods in favor of `Callback<PublishResult>`.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelMessageEditTest.java`:
- Around line 131-139: The test creates a Helpers.AsyncWaiter<PublishResult>
named publishResultAsyncWaiter but never passes it to channel.publish(), so
publishResultAsyncWaiter.waitFor() blocks; fix by calling channel.publish(...)
with publishResultAsyncWaiter as the callback parameter (i.e., pass
publishResultAsyncWaiter or its callback method into the channel.publish
invocation used to publish the "test_event" so PublishResult is delivered and
waitFor() can complete), then use publishResultAsyncWaiter.result to assert
serials.
🧹 Nitpick comments (2)
lib/src/test/java/io/ably/lib/test/rest/RestChannelMessageEditTest.java (1)

62-63: Consider adding null safety for serials array.

PublishResult.serials is annotated as @Nullable. Accessing publishResult.serials[0] directly risks an NPE if the server returns a null serials array. Consider asserting serials != null first:

assertNotNull("Expected serials array", publishResult.serials);
assertNotNull("Expected message to have a serial", publishResult.serials[0]);

This pattern should be applied consistently throughout the test file.

lib/src/main/java/io/ably/lib/realtime/ChannelBase.java (1)

447-455: Consider renaming for clarity.

The method is named callCompletionListenerError but accepts a Callback<PublishResult> parameter. Consider renaming to callCallbackError or similar to avoid confusion with the existing CompletionListener-based overload at line 437.

♻️ Suggested rename
-    private static void callCompletionListenerError(Callback<PublishResult> listener, ErrorInfo err) {
+    private static void callCallbackError(Callback<PublishResult> listener, ErrorInfo err) {

Comment on lines +131 to +139
Helpers.AsyncWaiter<PublishResult> publishResultAsyncWaiter = new Helpers.AsyncWaiter<>();

// Publish a message
channel.publish("test_event", "Original message data");

// Get the message from history to obtain its serial
PaginatedResult<Message> history = waitForMessageAppearInHistory(channel);
assertNotNull("Expected non-null history", history);
assertEquals(1, history.items().length);

Message publishedMessage = history.items()[0];
assertNotNull("Expected message to have a serial", publishedMessage.serial);
publishResultAsyncWaiter.waitFor();
PublishResult publishResult = publishResultAsyncWaiter.result;
assertEquals("Expected to have one serial", 1, publishResult.serials.length);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical bug: Missing callback causes test timeout.

The publishResultAsyncWaiter is created but never passed to channel.publish(). This causes waitFor() on line 137 to hang indefinitely, explaining the 300-second timeout in the pipeline.

🐛 Proposed fix
-        // Publish a message
-        channel.publish("test_event", "Original message data");
+        // Publish a message
+        channel.publish("test_event", "Original message data", publishResultAsyncWaiter);
🧰 Tools
🪛 GitHub Actions: Integration Test

[error] 137-137: Test timed out after 300 seconds while updating encoded data in updateMessage_updateEncodedData.

🤖 Prompt for AI Agents
In
`@lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelMessageEditTest.java`
around lines 131 - 139, The test creates a Helpers.AsyncWaiter<PublishResult>
named publishResultAsyncWaiter but never passes it to channel.publish(), so
publishResultAsyncWaiter.waitFor() blocks; fix by calling channel.publish(...)
with publishResultAsyncWaiter as the callback parameter (i.e., pass
publishResultAsyncWaiter or its callback method into the channel.publish
invocation used to publish the "test_event" so PublishResult is delivered and
waitFor() can complete), then use publishResultAsyncWaiter.result to assert
serials.

* @param data the message payload
* @throws AblyException
*/
public void publish(String name, Object data) throws AblyException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar NonBlocking annotations for all publish methods

* @return A {@link PublishResult} containing the message serial(s)
* @throws AblyException
*/
public PublishResult publishWithResult(String name, Object data) throws AblyException {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a blocking method?
Also do we have different naming for REST publish i.e. publishWithResult instead of plain publish

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for returning message serials from publish operations. It introduces new publishWithResult and publishAsync APIs that return PublishResult objects containing message serials, and deprecates older CompletionListener-based methods in favor of Callback<PublishResult>.

Changes:

  • Introduced publishWithResult synchronous API that returns PublishResult with message serials
  • Added new publishAsync methods accepting Callback<PublishResult> for asynchronous publishing with typed results
  • Deprecated CompletionListener-based publish methods in favor of callback-based variants with better type safety

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
lib/src/main/java/io/ably/lib/rest/ChannelBase.java Added publishWithResult and publishAsync(Callback<PublishResult>) methods; deprecated older CompletionListener variants
lib/src/main/java/io/ably/lib/realtime/ChannelBase.java Added new publish methods with Callback<PublishResult> parameter; deprecated CompletionListener variants
lib/src/main/java/io/ably/lib/realtime/CompletionListener.java Made ToCallback generic to support different result types
lib/src/main/java/io/ably/lib/push/PushBase.java Updated to use generic ToCallback<> with type parameter
android/src/main/java/io/ably/lib/push/PushChannel.java Updated to use generic ToCallback<> with type parameter
lib/src/test/java/io/ably/lib/test/rest/RestChannelMessageEditTest.java Updated tests to use publishWithResult and publishAsync with callback
lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelTest.java Simplified publish calls by removing explicit null listeners
lib/src/test/java/io/ably/lib/test/realtime/RealtimeChannelMessageEditTest.java Updated tests to use async publish with PublishResult callback

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +131 to +139
Helpers.AsyncWaiter<PublishResult> publishResultAsyncWaiter = new Helpers.AsyncWaiter<>();

// Publish a message
channel.publish("test_event", "Original message data");

// Get the message from history to obtain its serial
PaginatedResult<Message> history = waitForMessageAppearInHistory(channel);
assertNotNull("Expected non-null history", history);
assertEquals(1, history.items().length);

Message publishedMessage = history.items()[0];
assertNotNull("Expected message to have a serial", publishedMessage.serial);
publishResultAsyncWaiter.waitFor();
PublishResult publishResult = publishResultAsyncWaiter.result;
assertEquals("Expected to have one serial", 1, publishResult.serials.length);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The publishResultAsyncWaiter is created on line 131 but the publish call on line 134 does not pass it as an argument. This means waitFor() on line 137 will wait indefinitely and publishResult will be null. The publish call should pass publishResultAsyncWaiter as the third argument to enable the callback to populate the result.

Copilot uses AI. Check for mistakes.
* Asynchronously publish an array of messages on this channel
*
* @param messages the message
* @param listener a listener to be notified of the outcome of this message.
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation parameter name is inconsistent. The parameter is named "listener" but should be "callback" to match the actual parameter name and type (Callback) in the method signature.

Suggested change
* @param listener a listener to be notified of the outcome of this message.
* @param callback a callback to be notified of the outcome of this message.

Copilot uses AI. Check for mistakes.
assertEquals(1, history.items().length);
// Verify version history
assertNotNull("Expected non-null versions", versions);
assertTrue("Expected at least 2 versions (original + 2 updates)", versions.items().length >= 2);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test comment states "Expected at least 2 versions (original + 2 updates)" but only 1 update is performed in the test (updateMessage1). The comment should read "Expected at least 2 versions (original + 1 update)" to accurately reflect the test logic.

Suggested change
assertTrue("Expected at least 2 versions (original + 2 updates)", versions.items().length >= 2);
assertTrue("Expected at least 2 versions (original + 1 update)", versions.items().length >= 2);

Copilot uses AI. Check for mistakes.
* @param listener A listener may optionally be passed in to this call to be notified of success or failure of the operation.
* <p>
* This listener is invoked on a background thread.
* @throws AblyException
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The javadoc for this deprecated method is missing the @deprecated tag. For consistency with other deprecated methods in this file (lines 1079 and 1117), this method's javadoc should include "@deprecated Use {@link #publish(Message[], Callback)} instead."

Suggested change
* @throws AblyException
* @throws AblyException
* @deprecated Use {@link #publish(Message[], Callback)} instead.

Copilot uses AI. Check for mistakes.
try {
listener.onError(err);
} catch(Throwable t) {
Log.e(TAG, "Unexpected exception calling CompletionListener", t);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log message refers to "CompletionListener" but this method handles Callback, not CompletionListener. The error message should be updated to "Unexpected exception calling Callback" for accuracy.

Suggested change
Log.e(TAG, "Unexpected exception calling CompletionListener", t);
Log.e(TAG, "Unexpected exception calling Callback", t);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants