Skip to content

Commit

Permalink
Add connection and channel whenState methods
Browse files Browse the repository at this point in the history
These methods were added in ably-js a long time ago (first mention is in commit
55b1be4 there), being described as an "undocumented feature" used by a test
case. We seem to now consider them a part of the public API of ably-js; for
example, they were added to the TypeScript typings in [1].

The implemention of these methods, however, is inconsistent and flawed. As of
ably-js commit 64caf8e:

- when Connection is already in the given state, whenState synthesises a state
  change, and this state change is invalid (see [2])

- on the other hand, when RealtimeChannel is already in the given state,
  whenState emits a nullish state change, contrary to what its signature claims
  (see [3])

I suggested in [4] that we simply remove these methods in ably-js v2, but Simon
indicated that he considers these to be useful APIs worth considering keeping:

> IMO it's quite useful to be able to write `await
> realtime.connection.whenState('connected');` without having to worry about the
> edge case that the connection is already connected.

So, here I add them to the specification so that we can agree on the intended
behaviour and then test the implementation.

I’ve chosen to emit a null state change when the receiver is already in the
given state. This is in line with the already-attached behaviour described for
`RealtimeChannel#attach()` in RTL4d1, and I think makes more sense than the
alternatives of trying to synthesise a state change, or returning the state
change that led to the given state (this latter approach wouldn’t work for the
initial state anyway).

We’ll implement the behaviour specified here in ably-js v2, and I’ll figure out
what’s the best we can do in v1.

[1] ably/ably-js#444
[2] ably/ably-js#1599
[3] ably/ably-js#1598
[4] ably/ably-js#1597
  • Loading branch information
lawrence-forooghian committed Feb 26, 2024
1 parent 8376516 commit d5e8fc6
Showing 1 changed file with 8 additions and 0 deletions.
8 changes: 8 additions & 0 deletions textile/features.textile
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,9 @@ h3(#realtime-connection). Connection
** @(RTN23b)@ When initiating a connection, the client may send a @heartbeats@ param in the querystring, with value @true@ or @false@. If the value is true, the server will use Ably protocol messages (for example, a message with a @HEARTBEAT@ action) to satisfy the @maxIdleInterval@ requirement. If it is false or unspecified, the server is permitted to use any transport-level mechanism (for example, "websocket":https://ably.com/topic/websockets ping frames) to satisfy this. So for example, for "websocket transports":https://ably.com/topic/websockets, if the client is able to observe websocket pings, then it should send @heartbeats=false@. If not, it should send @heartbeats=true@.
* @(RTN24)@ A connected client may receive a @CONNECTED@ @ProtocolMessage@ from Ably at any point (though is typically triggered by a reauth, see @RTC8a@). The @connectionDetails@ in the @ProtocolMessage@ must override any stored details, see @RTN21@. The @Connection@ should emit an @UPDATE@ event with a @ConnectionStateChange@ object, which should have both @previous@ and @current@ attributes set to @CONNECTED@, and the @reason@ attribute set to to the @error@ member of the @CONNECTED@ @ProtocolMessage@ (if any). (Note that @UPDATE@ should be the only event emitted: in particular, the library must not emit an @CONNECTED@ event if the client was already connected, see @RTN4h@).
* @(RTN25)@ @Connection#errorReason@ attribute is an optional @ErrorInfo@ object which is set by the library when an error occurs on the connection, as described by "RSA4c1":#RSA4c1, "RSA4d":#RSA4d, "RTN11d":#RTN11d, "RTN14a":#RTN14a, "RTN14b":#RTN14b, "RTN14e":#RTN14e, "RTN14g":#RTN14g, "RTN15c7":#RTN15c7, "RTN15c4":#RTN15c4, "RTN15d":#RTN15d, "RTN15h":#RTN15h, "RTN15i":#RTN15i, "RTN16e":#RTN16e.
* @(RTN26)@ @Connection#whenState@ function:
** @(RTN26a)@ If the connection is already in the given state, calls the listener with a `null` argument.
** @(RTN26b)@ Else, calls @#once@ with the given state and listener.

h3(#realtime-channels). Channels

Expand Down Expand Up @@ -714,6 +717,9 @@ h3(#realtime-channel). RealtimeChannel
** @(RTL22c)@ The listener must only execute if all provided criteria are met.
** @(RTL22d)@ The method should use the @MessageFilter@ object if possible and idiomatic for the language.
* @(RTL24)@ @RealtimeChannel#errorReason@ attribute is an optional @ErrorInfo@ object which is set by the library when an error occurs on the channel, as described by "RTN11d":#RTN11d, "RTL3a":#RTL3a, "RTL4e":#RTL4e, "RTL4g":#RTL4g, "RTL14":#RTL14.
* @(RTL25)@ @RealtimeChannel#whenState@ function:
** @(RTL25a)@ If the channel is already in the given state, calls the listener with a `null` argument.
** @(RTL25b)@ Else, calls @#once@ with the given state and listener.

h3(#realtime-presence). RealtimePresence

Expand Down Expand Up @@ -1935,6 +1941,7 @@ class RealtimeChannel: // RTL*
name: String // RTL23
errorReason: ErrorInfo? // RTL24
state: ChannelState // RTL2b
whenState(ChannelState, (ChannelStateChange?) ->) // RTL25
presence: RealtimePresence // RTL9
properties: ChannelProperties // RTL15
// Only on platforms that support receiving push notifications:
Expand Down Expand Up @@ -2182,6 +2189,7 @@ class Connection: // RTN*
key: String? // RTN9
createRecoveryKey(): String? // RTN16g
state: ConnectionState // RTN4d
whenState(ConnectionState, (ConnectionStateChange?) ->) // RTN26
close() // RTN12
connect() // RTC1b, RTN3, RTN11
ping() => io Duration // RTN13
Expand Down

0 comments on commit d5e8fc6

Please sign in to comment.