Skip to content

[Split 2/3] Add io.ably.pubsub:device and :server door packages with side-declaring agents - #1233

Merged
ttypic merged 5 commits into
integration/v2from
integration/split-2-door-packages
Sep 16, 2026
Merged

ttypic merged 5 commits into
integration/v2from
integration/split-2-door-packages

Conversation

@umair-ably

@umair-ably umair-ably commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Second PR in the PDR-091b split stack (stacked on #1232; diff shows only this PR's changes). Reference: ably-js#2293, whose packages/shared/side.ts contract this ports.

What this PR does

  • io.ably.pubsub:server (jar → depends on :core): PubSubServer.httpClientBuilder(...) / PubSubServer.realtimeClientBuilder(...), per the door names agreed in PDR-091b2. Builders accept everything the core constructors accept (ClientOptions, API key string, token string — reusing the core's colon-rule disambiguation) and stamp the side at build().
  • io.ably.pubsub:device (aar → depends on :core-android): PubSubDevice.clientBuilder(...) — one door, per PDR-091 (device-side connectionless ops stay available on the one client).
  • Side-agent contract in a shared source dir (shared/, compiled into both door artifacts, not published — the Java analogue of ably-js's packages/shared/side.ts):
    • identifiers ably-pubsub-device / ably-pubsub-server, with the load-bearing-suffix warning comment (the -server suffix is what earns the MAU exemption on API-key auth);
    • caller agents entries preserved; the side entry is applied last and cannot be overridden; the caller's ClientOptions is never mutated; null passes through to the core's own initialization error.
    • The core keeps its ably-java/<version> base identifier — unchanged, already in the ably-common registry.
  • Fixes ClientOptions.copy(), which silently dropped headers, fallbackHosts, transportParams and agents. The doors rely on copy() for non-mutating stamping; the pre-existing internal callers of copy() also benefit. Unit-tested.

Tests (what billing reads — they fail loudly)

  • server: unit tests for stamping/preservation/override/no-mutation/null, plus a wire-level test that spins a local HTTP server and asserts the actual Ably-Agent header contains ably-pubsub-server/2.0.0 and ably-java/. Runs in check.yml via the existing unqualified runUnitTests invocation.
  • device: instrumentation tests asserting the same contract, added to the emulate.yml matrix (:device:connectedAndroidTest).

Open items flagged for review

  • ably-pubsub-device / ably-pubsub-server are not yet in the ably-common agents registry (checked protocol/agents.json on main, 2026-09-01) — needs an ably-common PR before any release; identifiers here match the ably-js branch exactly.
  • Builder surface is deliberately minimal (no fluent option setters yet) pending sign-off on the door surface with the chat-kotlin owner.
  • Both door artifacts bundle an identical io.ably.pubsub.internal.Side class — same pattern as the existing lib/ source shared between the two cores; harmless if both artifacts ever meet on a classpath.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added dedicated device and server SDK entry points for building authenticated REST and realtime clients.
    • Device and server clients now identify their SDK side in connection and request metadata.
    • Added published device and server library modules with streamlined builder APIs.
  • Bug Fixes

    • Client option copies now retain headers, fallback hosts, transport settings, and agent information.
    • Caller-provided agent metadata is preserved while SDK-specific side identifiers are applied consistently.

umair-ably added a commit to ably/ably-common that referenced this pull request Sep 1, 2026
PDR-091b splits every Pub/Sub SDK into per-side packages whose
factories stamp a side-declaring agent entry so that MAU
classification never has to guess which side a connection is on. The
identifiers are shared across languages: ably/ably-pubsub-js#2293,
ably/ably-pubsub-java#1233 and ably/ably-pubsub-ruby#453 all stamp the same
ably-pubsub-device / ably-pubsub-server strings, each with its own
package version, alongside the SDK's own agent entry.

Register both identifiers so the realtime system classifies them as
known agents rather than by their -device/-server suffix alone. Names
are language-neutral because the identifiers are.

The schema requires a single source repository for wrapper-type
agents, but these are the registry's first multi-repo identifiers;
ably-js is given as the first publisher, with the caveat noted in the
PR that fetch-agent-releases will attribute all stamped versions to
ably-js releases until the schema can express multiple sources.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 4dd7b496-025f-44a1-8197-7445cf5c1c27

📥 Commits

Reviewing files that changed from the base of the PR and between d22cbf0 and 9642ca8.

📒 Files selected for processing (16)
  • .github/workflows/emulate.yml
  • device/build.gradle.kts
  • device/gradle.properties
  • device/src/androidTest/java/io/ably/pubsub/device/PubSubDeviceTest.java
  • device/src/main/java/io/ably/pubsub/device/PubSubDevice.java
  • lib/src/main/java/io/ably/lib/transport/Defaults.java
  • lib/src/main/java/io/ably/lib/types/ClientOptions.java
  • lib/src/test/java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java
  • lib/src/test/java/io/ably/lib/types/ClientOptionsTest.java
  • pubsub-adapter/src/test/kotlin/com/ably/pubsub/SdkWrapperAgentHeaderTest.kt
  • server/build.gradle.kts
  • server/gradle.properties
  • server/src/main/java/io/ably/pubsub/server/PubSubServer.java
  • server/src/test/java/io/ably/pubsub/server/PubSubServerTest.java
  • settings.gradle.kts
  • shared/src/main/java/io/ably/pubsub/internal/Side.java

Disabled knowledge base sources:

  • Jira integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

The change adds device and server SDK modules, side-specific agent stamping, shared option handling, publication metadata, tests, and Gradle integration. The base agent identifier changes from ably-java to ably-pubsub-java.

Changes

SDK entry points and agent stamping

Layer / File(s) Summary
Agent identity and option propagation
lib/src/main/java/..., shared/src/main/java/..., lib/src/test/..., pubsub-adapter/src/test/...
The base agent identifier changes to ably-pubsub-java. ClientOptions.copy() retains additional option fields. Side stamps device or server agents while preserving caller agents and the original options.
Server SDK builders and validation
server/build.gradle.kts, server/gradle.properties, server/src/main/java/..., server/src/test/...
The server module adds publication metadata, HTTP and realtime builders, credential handling, server-agent stamping, and tests for construction and wire headers.
Device SDK builder and instrumentation
device/build.gradle.kts, device/gradle.properties, device/src/main/java/..., device/src/androidTest/...
The device module adds Android configuration, publication metadata, a realtime builder, device-agent stamping, and instrumented tests.
Module registration and emulator coverage
settings.gradle.kts, .github/workflows/emulate.yml
The Gradle build registers the new modules. The emulator workflow runs :device:connectedAndroidTest in addition to the existing Android test task.

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant PubSubServer
  participant Side
  participant AblyRealtime
  Caller->>PubSubServer: provide options or key/token
  PubSubServer->>Side: resolve and stamp server agent
  Side-->>PubSubServer: return stamped options
  PubSubServer->>AblyRealtime: construct realtime client
  AblyRealtime-->>Caller: return client
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch integration/split-2-door-packages

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

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

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

umair-ably added a commit to ably/ably-common that referenced this pull request Sep 2, 2026
PDR-091b splits every Pub/Sub SDK into per-side packages whose
factories stamp a side-declaring agent entry so that MAU
classification never has to guess which side a connection is on. The
identifiers are shared across languages: ably/ably-pubsub-js#2293,
ably/ably-pubsub-java#1233 and ably/ably-pubsub-ruby#453 all stamp the same
ably-pubsub-device / ably-pubsub-server strings, alongside the SDK's
own versioned agent entry.

The entries are versionless, like browser: under lockstep versioning a
side-flag version would always duplicate the SDK entry beside it, and
across SDKs a bare ably-pubsub-device/1.2.3 cannot say 1.2.3 of what.
Version-of-what stays the SDK entry's job; the side entry is a pure
flag. They carry no source or product for the same reason browser
carries none: they are emitted by many repos and attribute no package.

Register both so the realtime system classifies them as known agents
rather than by their -device/-server suffix alone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@umair-ably

Copy link
Copy Markdown
Contributor Author

Updated for ably/ably-common#361, mirroring ably/ably-js#2297: the side-declaring agent entries are now versionless flags — bare tokens on the wire, like browser — with identity/version continuing to travel on the SDK's own ably-java/<version> entry:

ably-java/2.0.0 jre/17 ably-pubsub-server

Unlike ably-js (which needed a getAgentString fix for name/undefined), our AgentHeaderCreator already emits a null-valued map entry as a bare token, so no core change was needed — the doors stamp null and drop their BuildConfig version plumbing. Tests now assert the flag appears as a bare token and fail if any /version form regresses, and that each side's client doesn't carry the other side's flag.

@umair-ably

Copy link
Copy Markdown
Contributor Author

Added the family-identifier half of the agent convention (mirrors ably-js#2297): Defaults.ABLY_AGENT_VERSION now stamps ably-pubsub-java/<version>, with the four identifier assertions updated (RealtimeHttpHeaderTest canary, PubSubServerTest, Side javadoc). Verified locally: :server:test and :core:test --tests '*RealtimeHttpHeader*' pass on JDK 17. Registry entries for both the family identifier and the versionless side flags are in ably-common#361, which should merge before any release from this branch.

umair-ably added a commit to ably/ably-pubsub-ruby that referenced this pull request Sep 2, 2026
The family identifier follows the package split, per the agent
identifier convention proposed on PDR-091b2 and registered in
ably/ably-common#361: because it flips exactly at the split and the
maintenance branch is never touched, the identifier alone partitions
the fleet - ably-ruby/* is legacy-gem traffic, ably-pubsub-ruby/* is
new-package traffic. It names the family rather than any one published
gem; the side a client declares travels as the separate versionless
entry stamped by the server factories:

  ably-pubsub-ruby/2.0.0 ruby/3.3.0 ably-pubsub-server

Requires the ably-pubsub-ruby registry entry (ably/ably-common#361)
before any release from this branch. Mirrors ably/ably-pubsub-js#2297 and
ably/ably-pubsub-java#1233.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from integration/split-1-core-modules to integration/v2 September 16, 2026 11:36
umair-ably and others added 4 commits September 16, 2026 12:36
Adds the two public artifacts of the PDR-091b split, following the
ably-js reference implementation (ably-js#2293):

- io.ably.pubsub:server (jar, on :core): PubSubServer.httpClientBuilder()
  and PubSubServer.realtimeClientBuilder(), each accepting everything the
  core constructors accept (ClientOptions, API key or token string).
- io.ably.pubsub:device (aar, on :core-android): PubSubDevice.clientBuilder(),
  one door per PDR-091.
- A shared side helper (shared/src/main/java, compiled into both door
  artifacts rather than published) owns the ably-pubsub-device and
  ably-pubsub-server agent identifiers and the stamping rules: caller
  entries preserved, side entry applied last and unoverridable, caller's
  options never mutated, null passing through to the core's own error.
  The -device/-server suffixes are load-bearing for MAU billing
  classification and documented as such.
- Fixes ClientOptions.copy() to carry headers, fallbackHosts,
  transportParams and agents, which it previously dropped; the doors rely
  on copy() for non-mutating stamping. Covered by a new unit test.
- Server tests include a wire-level assertion that the Ably-Agent HTTP
  header carries ably-pubsub-server/<version> alongside the ably-java
  base identifier; device instrumentation tests assert the same contract
  and run in the emulator matrix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mirrors ably-js#2297, matching the registry entries in ably-common#361:
the side flags are registered versionless (like `browser`), because under
lockstep versioning a side-flag version always duplicates the SDK entry
beside it, and across SDKs a bare version cannot say what it versions.
Identity, version and support status keep travelling on the SDK's own
ably-java/<version> entry. Wire shape:

  ably-java/2.0.0 jre/17 ably-pubsub-server

AgentHeaderCreator already emits a null-valued map entry as a bare token,
so no core change is needed; the doors stamp null and drop their
BuildConfig version plumbing. Tests now assert the flag is present as a
bare token and fail if any /version form regresses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The family identifier follows the package split, per the agent
identifier convention proposed on PDR-091b2 and registered in
ably/ably-common#361: because it flips exactly at the split and the
maintenance branch is never touched, the identifier alone partitions
the fleet - ably-java/* is legacy-package traffic, ably-pubsub-java/*
is new-package traffic. It names the family rather than any one
published artifact; the side a client declares travels as the separate
versionless entry stamped by the door packages:

  ably-pubsub-java/2.0.0 jre/17.0.12 ably-pubsub-server

Requires the ably-pubsub-java registry entry (ably/ably-common#361)
before any release from this branch. Mirrors ably/ably-pubsub-js#2297.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comments describe the behavior in place (versionless registry entries,
the load-bearing suffix) rather than citing decision-record or PR
numbers; those references live in the PR descriptions and READMEs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ifier

SdkWrapperAgentHeaderTest asserts the full agent header and still
expected the pre-rename ably-java family identifier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ttypic
ttypic force-pushed the integration/split-2-door-packages branch from d1884bf to 9642ca8 Compare September 16, 2026 11:36
@ttypic
ttypic marked this pull request as ready for review September 16, 2026 11:42
@ttypic
ttypic merged commit d43a3a9 into integration/v2 Sep 16, 2026
11 of 15 checks passed
@ttypic
ttypic deleted the integration/split-2-door-packages branch September 16, 2026 11:42
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.

2 participants