diff --git a/package.json b/package.json index c16464d9..758ae314 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "@ably/specification-build", "version": "1.0.0", "scripts": { - "build": "npm-run-all build:generate build:tailwind", + "clean": "rm -rf ./output", + "build": "npm-run-all clean build:generate build:tailwind", "build:generate": "./scripts/build", "build:tailwind": "tailwindcss -i ./templates/main.css -o ./output/tailwind.css --minify", "lint": "npm-run-all format:*:check", diff --git a/textile/chat-features.textile b/textile/chat-features.textile index 9bb52afc..8d446f28 100644 --- a/textile/chat-features.textile +++ b/textile/chat-features.textile @@ -9,5 +9,495 @@ jump_to: h2(#overview). Overview -Section dedicated to new chat feature development. TBC +This document outlines the feature specification for the Ably Chat product. +The key words "must", "must not", "required", "shall", "shall not", "should", "should not", "recommended", "may", and "optional" (whether lowercased or uppercased) in this document are to be interpreted as described in "RFC 2119":https://tools.ietf.org/html/rfc2119. + +The over-the-wire protocol for Chat, as well as implementation details, may be found on the chat protocol page. + +h2(#overview). Chat Specification Version + +* @(CHA-V1)@ **Specification Version**: This document defines the Ably chat library features specification ('features spec'). +** @(CHA-V1a)@ The current version of the Chat library feature specification is version @0.1.0@. + +h2(#general). General Principles + +* @(CHA-GP1)@ As far as is practicable, the implementation details such as underlying Realtime Channels should be hidden from the public API. This allows developers to interact with Chat without having to understand many low-level primitives. +* @(CHA-GP2)@ The public API should avoid implicit operations as a side-effect to some other operation. For example, adding a subscriber to messages in a Chat Room should not automatically trigger that Room to attach. This is in contrast to the current core SDKs. Avoiding side-effects provides a clean, easy to understand API. +* @(CHA-GP3)@ Wherever possible, Chat features should be exposed in the public API as properties of their parent. For example, @messages@ would be considered a property of a @room@ object. This allows for greater composability and extensibility in the future. +* @(CHA-GP4)@ Avoid overloading methods and optional parameters. Prefer object-type parameters wherever practical and idiomatic. + +h2(#rooms). Rooms + +h3(#rooms-general). General Information + +A Room is the atomic unit of Chat. All chat operations are performed in the context of a room that a client is currently attached to. As the Room is the atomic unit, its state should be considered as the combination of the states of underlying resources. + +h3(#rooms-status). Room Statuses + +The status of any given Chat Room is the combination of the states of all of its constituent contributors. For more information on this, see "@Room Lifecycle@":#rooms-lifecycle. + +* @(CHA-RS1)@ Every room has a @status@, which describes the current status of the room. +** @(CHA-RS1a)@ The @INITIALIZED@ status is the initial status before any attachment operations have taken place. +** @(CHA-RS1b)@ The @ATTACHING@ status is used when the room is in the process of attaching to Ably. +** @(CHA-RS1c)@ The @ATTACHED@ status means that the room is fully connected to Ably and functioning normally. +** @(CHA-RS1d)@ The @DETACHING@ status is used when the room is in the process of detaching from Ably. +** @(CHA-RS1e)@ The @DETACHED@ status means that the room has been detached from Ably by a user-initiated request. It will not attempt to re-connect without explicit intervention. +** @(CHA-RS1f)@ The @SUSPENDED@ status is when the room has been detached from Ably for an extended period of time. The room will periodically attempt to reconnect. +** @(CHA-RS1g)@ The @FAILED@ status means that something has gone wrong with the room (such as lack of permissions). The room will not attempt to connect to Ably and will require user intervention. +** @(CHA-RS1h)@ The @RELEASING@ status means that the room is being released and the underlying resources are being cleaned up. +** @(CHA-RS1i)@ The @RELEASED@ status means that the room has been cleaned up and the object can no longer be used. +* @(CHA-RS2)@ A room must expose its current status. +** @(CHA-RS2a)@ @[Testable]@ A room must expose its current status, a single value from the list provided above. +** @(CHA-RS2b)@ A room must expose the latest error, if any, associated with its current status. +*** @(CHA-RS2b1)@ @[Testable]@ If an error is associated with the current status, then this must be exposed. +*** @(CHA-RS2b2)@ @[Testable]@ If there is no error is associated with the current status, then the room status should not expose any errors. +* @(CHA-RS3)@ @[Testable]@ A newly created room must have a current status of @INITIALIZED@. +* @(CHA-RS4)@ A room must allow changes to room status to be observed by clients. +** @(CHA-RS4a)@ @[Testable]@ Room status update events must contain the newly entered room status. +** @(CHA-RS4b)@ @[Testable]@ Room status update events must contain the previous room status. +** @(CHA-RS4c)@ @[Testable]@ Room status update events must not contain an error, where that error pertains to the current status. +** @(CHA-RS4d)@ @[Testable]@ Room status update events must contain an error, where that error pertains to the current status. +** @(CHA-RS4e)@ @[Testable]@ Clients must be able to register a listener for room status updates and receive such events. +** @(CHA-RS4f)@ @[Testable]@ Clients must be able to unregister a listener for room status updates and from that point onwards, cease to receive such events on that listener only. +** @(CHA-RS4g)@ @[Testable]@ Clients must be able to unregister all listeners for room status updates and from that point onwards, cease to receive such events on all listeners. + +h3(#rooms-lifecycle). Room Lifecycle + +Rooms are considered the atomic unit of chat and comprise of potentially multiple underlying realtime channels. The status of the room is, broadly speaking, the combination of the respective underlying channel states (the rooms @contributors@). We present one unified status, rather than statuses for multiple individual features, as this is easier for developers to reason about. + +Chat features are spread across multiple Realtime Channels. For the purpose of this section, a @contributor@ is a feature in Chat that is considered as part of the overall room status, as well as its lifecycle. A @contributor@ might share its realtime channel with another (e.g. messages and occupancy), or it might be entirely standalone (room reactions). In that sense, implementations of Room Lifecycle @MUST@ make no assumptions over which realtime channels are in use or being shared, and treat every @contributor@ as being entirely standalone. + +There are four room lifecycle operations: @ATTACH@, @DETACH@, @RELEASE@ and @RETRY@. @RETRY@ is an internal-only operation. These operations are intended to be atomic and not interfere with each other, in order to preserve chat room integrity. How this is achieved is described in further specification items. + +Discontinuities in Realtime connections happen - whereby continuity of message delivery is disrupted. Therefore each feature may also experience discontinuity events - where the user may need to take some action to restore continuity in their application. In Chat, we explicitly tell the user when there's a discontinuity, rather than require them to implement the monitoring themselves. As such are events are normally associated with something going wrong, we only want to tell them that a discontinuity happened when everything has fixed itself. Therefore, when a discontinuity is noticed by us, we might not notify the user immediately, instead preferring to hold the event in pending until the right time. For more information on how to handle discontinuities, consult the room lifecycle specification points. + +In the same sense as discontinuities, sometimes connections drop momentarily - due to connection balancing by the Realtime system or simply bad internet connections. In Chat we try to avoid broadcasting these transient disconnects to users, so long as they don't affect continuity. Therefore, we tend to handle disconnections or detachments optimistically at first - hoping that they will resolve themselves, and only informing the user when it is clear that it may take longer than anticipated. + +h4(#rooms-lifecycle-operations). Room Lifecycle Operations + +* @(CHA-RL1)@ A room must be explicitly attached via the @ATTACH@ operation. +** @(CHA-RL1a)@ @[Testable]@ If the room is already in the @ATTACHED@ status, then this operation is no-op. +** @(CHA-RL1b)@ @[Testable]@ If the room is in the @RELEASING@ status, the operation shall be rejected with an @ErrorInfo@ with the @RoomIsReleasing@ error code from the "chat-specific error codes":#error-codes. +** @(CHA-RL1c)@ @[Testable]@ If the room is in the @RELEASED@ status, the operation shall be rejected with an @ErrorInfo@ with the @RoomIsReleased@ error code from the "chat-specific error codes":#error-codes. +** @(CHA-RL1d)@ @[Testable]@ If a room lifecycle operation is already in progress, this operation shall wait until the current operation completes before continuing. +** @(CHA-RL1e)@ @[Testable]@ Notwithstanding the above points, when the attachment operation begins, the room shall be transitioned to the @ATTACHING@ status. +** @(CHA-RL1f)@ @[Testable]@ The underlying @contributors@ will have their Realtime Channels attached in sequence - an attach call must complete successfully before the next is started. +** @(CHA-RL1g)@ When all @contributors@ Realtime Channels successfully attach (the calls to @attach()@ complete successfully), the operation is now complete and the room is considered attached. +*** @(CHA-RL1g1)@ @[Testable]@ The room status shall be transitioned to @ATTACHED@. +*** @(CHA-RL1g2)@ @[Testable]@ Any contributors to room that have a pending discontinuity event against them, must be notified of this fact, using the error that caused the original discontinuity. +*** @(CHA-RL1g3)@ @[Testable]@ Any transient disconnect timeouts shall be cleared. +** @(CHA-RL1h)@ If a one of the @contributors@ Realtime Channels fails to attach (i.e. the call to @attach()@ returns an error), then the operation has failed and must be rolled back. The procedure to roll back is described in subsequent points. +*** @(CHA-RL1h1)@ @[Testable]@ The @attach@ call must throw an @ErrorInfo@. The code for this error is determined by the feature that failed (see "here":#error-codes for more information on error codes). +*** @(CHA-RL1h2)@ @[Testable]@ If the underlying Realtime Channel entered the @SUSPENDED@ state (i.e. the @attach()@ operation fails and upon subsequently checking the channel state, it is @SUSPENDED@), then the room status will transition to @SUSPENDED@. The state transition @ErrorInfo@ will use the Realtime Channel error as the @cause@. The status code shall be @500@ and the error code whatever attachment error code corresponds to the feature that has failed, per "the error code list":#error-codes. +*** @(CHA-RL1h3)@ @[Testable]@ When the room enters the @SUSPENDED@ status as a result of @CHA-RL1h2@ the @attach()@ operation shall fail using the error from @CHA-RL1h2@. The room lifecycle shall asynchronously (non-blocking to the @ATTACH@ operation) enter the recovery loop (per "@CHA-RL5@":#CHA-RL5) and attempt to re-establish attachment. +*** @(CHA-RL1h4)@ @[Testable]@ If the underlying Realtime Channel entered the @FAILED@ state, then the room status will transition to @FAILED@, using the error from the realtime channels `attach()` call as the @cause@ field of the @ErrorInfo@. The state transition @ErrorInfo@ will use the Realtime Channel error as the @cause@. The status code shall be @500@ and the error code whatever attachment error code corresponds to the feature that has failed, per "the error code list":#error-codes. The same error shall be thrown as the result of the @ATTACH@ operation. +*** @(CHA-RL1h5)@ @[Testable]@ When the room enters the @FAILED@ status as a result of @CHA-RL1h4@, asynchronously with respect to @CHA-RL1h4@, then the room will detach all channels that are not in the @FAILED@ state. +*** @(CHA-RL1h6)@ @[Testable]@ If, when performing @CHA-RL1h5@, a channel fails to detach on command, then the detach operation will be repeated until such a time that all channels have detached successfully. +** @(CHA-RL1i)@ Because of the singleton pattern that the core SDKs implement with regards to channels, subsequent instances of a room will use the same underlying realtime channels if the realtime channels are not properly @released@ as part of cleaning up the previous room instance before the new instance is used. Therefore, it is important to ensure that releasing operations (per "@CHA-RC1d@"#CHA-RC1d) have completed before a new room can be attached (to avoid channel objects leaking between instances of rooms). +*** @(CHA-RL1i1)@ @[Testable]@ If a room instance is in the process of being released and cleaned up (per "@CHA-RC1d@"#CHA-RC1d), then the @ATTACH@ operation of a subsequent instance of the same room shall wait for the release operation of the previous to complete before commencing. +* @(CHA-RL2)@ A room must be explicitly detached via the @DETACH@ operation. +** @(CHA-RL2a)@ @[Testable]@ If the room status is already @DETACHED@, then this operation is a no-op. +** @(CHA-RL2b)@ @[Testable]@ If the room is in the @RELEASING@ status, the operation shall be rejected with an @ErrorInfo@ with the @RoomIsReleasing@ error code from the "chat-specific error codes":#error-codes. +** @(CHA-RL2c)@ @[Testable]@ If the room is in the @RELEASED@ status, the operation shall be rejected with an @ErrorInfo@ with the @RoomIsReleased@ error code from the "chat-specific error codes":#error-codes. +** @(CHA-RL2d)@ @[Testable]@ If the room is in the @FAILED@ status, the operation shall be rejected with an @ErrorInfo@ with @RoomInFailedState@ error code from the "chat-specific error codes":#error-codes. +** @(CHA-RL2e)@ @[Testable]@ Notwithstanding the above points, when the detachment operation begins, the room shall be transitioned to the @DETACHING@ status and any transient disconnect timeouts cleared. +** @(CHA-RL2f)@ @[Testable]@ The underlying @contributors@ Realtime Channels will be detached in sequence - a call to @detach()@ must complete before the next call begins. +** @(CHA-RL2g)@ @[Testable]@ If all channel detachments complete successfully (all calls @detach()@ return successfully), then the room shall transition to the @DETACHED@ status. +** @(CHA-RL2h)@ If during detachment, a channel fails to detach, different operations are performed based on the nature of the detach. +*** @(CHA-RL2h1)@ @[Testable]@ If a channel enters the @FAILED@ state during detachment, then the room will transition to the @FAILED@ status. The detach operation continues until all channels are either @DETACHED@ or @FAILED@, with the operation throwing an @ErrorInfo@ with the feature-specific error code of the first feature to fail, using the underlying channel error as the @cause@. The same @ErrorInfo@ must accompany the @FAILED@ room status. +*** @(CHA-RL2h2)@ @[Testable]@ If the room is already in a @FAILED@ status during the detach operation and another channel fails, the @FAILED@ status will not be emitted twice. +*** @(CHA-RL2h3)@ @[Testable]@ If a channel enters another state during detachment (namely @ATTACHED@, which happens when detach times out). Then the detachment cycle will be retried after a short pause. The rooms status will not change during this process. +* @(CHA-RL3)@ A room must be explicitly released via the @RELEASE@ operation.. This operation is not performed directly on a Room object by the client, but is described here. +** @(CHA-RL3a)@ @[Testable]@ If the room is already in the @RELEASED@ status, this operation is no-op. +** @(CHA-RL3b)@ @[Testable]@ If the room is in the @DETACHED@ status, then the room is immediately transitioned to @RELEASED@ and the operation succeeds. +** @(CHA-RL3c)@ @[Testable]@ If the room is in the @RELEASING@ status, then the operation will return the result of the pending @release@ operation, or throw any exception that it throws. +** @(CHA-RL3c)@ @[Testable]@ When the release operation commences, the room will transition into the @RELEASING@ status any any transient disconnect timeouts shall be cleared. +** @(CHA-RL3d)@ @[Testable]@ All features channels are detached in sequence. +** @(CHA-RL3e)@ @[Testable]@ If a channel is in the @FAILED@ state when it is time to detach, it shall be ignored. +** @(CHA-RL3f)@ @[Testable]@ If a channel fails to detach, the channel detach sequence will be retried after a short wait. +** @(CHA-RL3g)@ @[Testable]@ Once all channels have entered the @DETACHED@ state, then the room state is transitioned to @RELEASED@ and the operation completes. +** @(CHA-RL3h)@ @[Testable]@ Upon operation completion, the underlying Realtime Channels are released from the core SDK prevent leakage. +* @(CHA-RL5)@ A room must @RETRY@ whenever it enters the @SUSPENDED@ state. +** @(CHA-RL5a)@ @[Testable]@ When entering a retry-loop, the room must first @DETACH@ all contributors underlying realtime channels. +** @(CHA-RL5b)@ @[Testable]@ If the operation above fails, then it must be retried after a short wait. +** @(CHA-RL5c)@ @[Testable]@ If the operation above fails because a channel has entered the @FAILED@ state, then the retry loop must stop and the room must be placed in the @FAILED@ status. +** @(CHA-RL5d)@ Once all channels have been detached, the room waits until the original channel that caused the retry loop naturally enters the @ATTACHED@ state. +** @(CHA-RL5e)@ @[Testable]@ If the channel state becomes @FAILED@, then the room status is transitioned to @FAILED@ and the retry loop stops. +** @(CHA-RL5f)@ @[Testable]@ If the channel state becomes @ATTACHED@, then the room status is transitioned to @ATTACHING@ to begin a new attachment cycle. +*** @(CHA-RL5f1)@ @[Testable]@ If the attachment cycle succeeds, then the room is transitioned to @ATTACHED@ and any discontinuity errors are broadcast to contributors. +*** @(CHA-RL5f2)@ @[Testable]@ If the attachment cycle fails because a channel entered @FAILED@, then the room is transitioned to @FAILED@ and the retry loop terminates. +*** @(CHA-RL5f3)@ @[Testable]@ If the attachment cycle fails because a channel entered @SUSPENDED@, then the room is transitioned to @SUSPENDED@ and the retry loop restarted using the @SUSPENDED@ channel as the subject. + +h4(#rooms-lifecycle-monitoring). Room Lifecycle Monitoring + +As well as user-initiated operations, the room must monitor its underlying resources and act accordingly. This includes handling transient or long-term disconnection from Ably, as well as discontinuities in channel messages. + +* @(CHA-RL4)@ A room must monitor the state of its @contributors@ Realtime Channels and act upon any changes. +** @(CHA-RL4a)@ The state monitor must handle @UPDATE@ events from each contributors underlying Realtime Channel +*** @(CHA-RL4a1)@ @[Testable]@ If the @resume@ flag of the update is set to @true@, then no action should be taken. +*** @(CHA-RL4a2)@ @[Testable]@ If the given contributor has not yet successfully managed to attach its Realtime Channel, then no action should be taken. +*** @(CHA-RL4a3)@ @[Testable]@ If a room lifecycle operation is in progress, then a pending discontinuity event shall be recorded for this contributor. The @ErrorInfo@ associated with the discontinuity shall be the @reason@ for the underlying channel state change. The event will be notified to the contributor at a later point, as noted in this specification. +*** @(CHA-RL4a4)@ @[Testable]@ If a room lifecycle operation is not in progress, then a discontinuity event will immediately be emitted to the contributor. The @ErrorInfo@ associated with the discontinuity shall be the @reason@ for the underlying channel state change. +** @(CHA-RL4b)@ The state monitor must handle other channel state events. +*** @(CHA-RL4b1)@ @[Testable]@ If a channel lifecycle operation is in progress, and the new channel state is @ATTACHED@, and the @resume@ flag is false, @and@ the particular contributor has been attached previously, then a pending discontinuity event will be recorded for the contributor. The error associated with this event shall be the @reason@ for the channel state change. +*** @(CHA-RL4b2)@ @[Testable]@ If a channel lifecycle operation is in progress and the new channel state is not @ATTACHED@, then no action is taken. +*** @(CHA-RL4b3)@ @[Testable]@ If a channel lifecycle operation is in progress, the channel state is @ATTACHED@ and the @resume@ flag is true, then no action is taken. +*** @(CHA-RL4b4)@ @[Testable]@ If a channel lifecycle operation is in progress, the channel state is @ATTACHED@, the @resume@ flag is false but the contributor has never previouly reached the state of @ATTACHED@, then no action is taken. +*** @(CHA-RL4b5)@ @[Testable]@ If a channel lifecycle operation is not in progress and the channel state is @FAILED@, then the room status shall be transitioned to @FAILED@, using the @reason@ for the channel state change as the @error@ for the room status change. All @transient disconnect timeouts@ are cancelled and any non-detached contributors are detached. +*** @(CHA-RL4b6)@ @[Testable]@ If a channel lifecycle operation is not in progress and the channel state is @ATTACHING@ and a transient disconnect timeout exists for the contributor, then no action is needed. +*** @(CHA-RL4b7)@ @[Testable]@ If a channel lifecycle operation is not in progress and the channel state is @ATTACHING@ and no transient disconnect timeout exists for the contributor, then a timeout is created with a 5 second limit. Upon timeout, the room status is transitioned to @ATTACHING@, using the @reason@ from the initial channel state change as the error for the transition. +*** @(CHA-RL4b7)@ @[Testable]@ If a channel lifecycle operation is not in progress and the channel state is @ATTACHED@ and a transient disconnect timeout exists for the contributor, the timeout is cleared. +*** @(CHA-RL4b8)@ @[Testable]@ If a channel lifecycle operation is not in progress, the channel state is @ATTACHED@, the room status is NOT @ATTACHED@ and all contibutors channel are now @ATTACHED@, the room status is transitioned to @ATTACHED@. +*** @(CHA-RL4b9)@ @[Testable]@ If a channel lifecycle operation is not in progress and the channel state is @SUSPENDED@, then the room status is transitioned to @SUSPENDED@, using the @reason@ of the channel state change as the error. Any transient disconnect timeouts are cancelled and the room enters the @RETRY@ loop. + + +h2(#room-configuration). Room Configuration + +* @(CHA-RC1)@ Chat Rooms are singleton objects with respect to the Chat Client on which they are created. +** @(CHA-RC1a)@ @[Testable]@ Requesting a room from the Chat Client will provide an instance of a room with the provided id. +** @(CHA-RC1b)@ @[Testable]@ Once a room has been created on the Chat Client, assuming it is not subsequently released, then the client will return the same room instance on subsequent calls for the same room ID. +** @(CHA-RC1c)@ @[Testable]@ If a room is requested with different options to an existing room of the same id, then an @ErrorInfo@ with code @40000@ is thrown. +** @(CHA-RC1d)@ @[Testable]@ If a room is requested to be released, it is immediately removed from the SDKs room map - meaning subsequent calls to get an instance of a room for the same ID shall produce a new object. +** @(CHA-RC1e)@ @[Testable]@ Pursuant to @CHA-RC1d@, the room releasing lifecycle operations (see "@CHA-RL3@"#CHA-RL3) shall occur @AFTER@ the room is removed from the room map, but shall complete before the call to release the room completes. +* @(CHA-RC2)@ Chat rooms are configurable, so as to enable or disable certain features. When requesting a room, options as to which features should be enabled, and the configuration they should take, must be provided. +** @(CHA-RC2a)@ @[Testable]@ If a room is requested with invalid configuration, for example: a negative typing timeout, an @ErrorInfo@ with code @40001@ must be thrown. +** @(CHA-RC2b)@ @[Testable]@ If a room is configured to have a given feature disabled, then attempting to use that feature must result in an @ErrorInfo@ with code @40000@ being thrown. + +h2(#messages). Messages + +Messages are the quintessential component of a chat room - the purpose of chat is for users to talk to each other! + +Broadly speaking, messages are published via REST calls to the Chat HTTP API and message events are received in Realtime over a corresponding realtime channel. + +@Messages@ shall be exposed to consumers via the @messages@ property of a @Room@. + +* @(CHA-M1)@ Chat messages for a Room are sent on a corresponding realtime channel @::$chat::$chatMessages@. For example, if your room id is @my-room@ then the messages channel will be @my-room::$chat::$chatMessages@. +* @(CHA-M2)@ A @Message@ corresponds to a single message in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: **not** a @ProtocolMessage@). +** @(CHA-M2a)@ @[Testable]@ A @Message@ is considered before another @Message@ in the global order if the @timeserial@ of the corresponding realtime channel message comes first. +** @(CHA-M2b)@ @[Testable]@ A @Message@ is considered after another @Message@ in the global order if the @timeserial@ of the corresponding realtime channel message comes second. +** @(CHA-M2c)@ @[Testable]@ A @Message@ is considered to be equal to another @Message@ if they have the same timeserial. +* @(CHA-M3)@ Messages are sent to Ably via the Chat REST API, using the @send@ method. +** @(CHA-M3a)@ @[Testable]@ When a message is sent successfully, the caller shall receive a struct representing the "@Message@":#chat-structs-message in response (as if it were received via Realtime event). +** @(CHA-M3b)@ @[Testable]@ A message may be sent without @metadata@ or @headers@. When these are not specified by the user, they must be omitted from the REST payload. +** @(CHA-M3c)@ @[Testable]@ @metadata@ must not contain the key @ably-chat@. This is reserved for future internal use. If this key is present, the send call shall terminate by throwing an @ErrorInfo@ with code @40001@. +** @(CHA-M3d)@ @[Testable]@ @headers@ must not contain a key prefixed with @ably-chat@. This is reserved for future internal use. If this key is present, the send call shall terminate by throwing an @ErrorInfo@ with code @40001@. +** @(CHA-M3e)@ @[Testable]@ If an error is returned from the REST API, its @ErrorInfo@ representation shall be thrown as the result of the @send@ call. +* @(CHA-M4)@ Messages can be received via a subscription in realtime. +** @(CHA-M4a)@ @[Testable]@ A subscription can be registered to receive incoming messages. Adding a subscription has no side effects on the status of the room or the underlying realtime channel. +** @(CHA-M4b)@ @[Testable]@ A subscription can de-registered from incoming messages. Removing a subscription has no side effects on the status of the room or the underlying realtime channel. +** @(CHA-M4c)@ @[Testable]@ When a realtime message with @name@ set to @message.created@ is received, it is translated into a message event, which contains a @type@ field with the event type as well as a @message@ field containing the "@Message Struct@":#chat-structs-message. This event is then broadcast to all subscribers. +** @(CHA-M4d)@ @[Testable]@ If a realtime message with an unknown @name@ is received, the SDK shall silently discard the message, though it may log at @DEBUG@ or @TRACE@ level. +** @(CHA-M5d)@ @[Testable]@ Incoming realtime events that are malformed (unknown field should be ignored) shall not be emitted to subscribers. +* @(CHA-M5)@ For a given subscription, messages prior to the point of subscription can be retrieved in a history-like request. Note that this is the point in the message flow @(subscription point)@ at which the subscription was made, NOT the channel attachment point. +** @(CHA-M5a)@ @[Testable]@ If a subscription is added when the underlying realtime channel is @ATTACHED@, then the @subscription point@ is the current @channelSerial@ of the realtime channel. +** @(CHA-M5b)@ @[Testable]@ If a subscription is added when the underlying realtime channel is in any other state, then its @subscription point@ becomes the @attachSerial@ at the the point of channel attachment. +** @(CHA-M5c)@ @[Testable]@ If a channel leaves the @ATTACHED@ state and then re-enters @ATTACHED@ with @resumed=false@, then it must be assumed that messages have been missed. The @subscription point@ of any subscribers must be reset to the @attachSerial@. +** @(CHA-M5d)@ @[Testable]@ If a channel @UPDATE@ event is received and @resumed=false@, then it must be assumed that messages have been missed. The @subscription point@ of any subscribers must be reset to the @attachSerial@. +** @(CHA-M5e)@ Each subscription shall expose a method or callback that allows for messages to be queried. These messages are queried via the "REST API"#rest-fetching-messages. +** @(CHA-M5f)@ @[Testable]@ This method must accept any of the standard history query options, except for @direction@, which must always be @backwards@. +** @(CHA-M5g)@ @[Testable]@ The subscribers @subscription point@ must be additionally specified (internally, by us) in the @fromSerial@ query parameter. +** @(CHA-M5h)@ @[Testable]@ The method must return a standard @PaginatedResult@ , which can be further inspected to paginate across results. +** @(CHA-M5i)@ @[Testable]@ If the REST API returns an error, then the method must throw its @ErrorInfo@ representation. +** @(CHA-M5j)@ @[Testable]@ If the @end@ parameter is specified and is more recent than the @subscription point@ timeserial, the method must throw an @ErrorInfo@ with code @40000@. +* @(CHA-M6)@ Messages should be queryable from a paginated REST API. +* @(CHA-M6a)@ @[Testable]@ A method must be exposed that accepts the standard Ably REST API query parameters. It shall call the "REST API"#rest-fetching-messages and return a @PaginatedResult@ containing messages, which can then be paginated through. +* @(CHA-M6b)@ @[Testable]@ If the REST API returns an error, then the method must throw its @ErrorInfo@ representation. +* @(CHA-M7)@ @[Testable]@ Users may subscribe to discontinuity events to know when there's been a break in messages that they need to resolve. Their listener will be called when a discontinuity event is triggered from the room lifecycle. + +h2(#reactions). Ephemeral Room Reactions + +Ephemeral room reactions are one-time events that are sent to the room, such as thumbs-up or heart emojis. They are supposed to capture the current emotions in the room (e.g. everyone spamming the :tada: emoji when a team scores the winning goal). + +They are ephemeral as we do not currently store these messages do not support server-authoritative counting. + +All ephemeral room reactions are handled over the Realtime connection. + +@Reactions@ shall be exposed to consumers via the @reactions@ property of a @Room@. + +* @(CHA-ER1)@ Reactions for a Room are sent on a corresponding realtime channel @::$chat::$roomReactions@. For example, if your room id is @my-room@ then the reactions channel will be @my-room::$chat::$roomReactions@. +* @(CHA-ER2)@ A @Reaction@ corresponds to a single reaction in a chat room. This is analogous to a single user-specified message on an Ably channel (NOTE: **not** a @ProtocolMessage@). +* @(CHA-ER3)@ Ephemeral room reactions are sent to Ably via the Realtime connection via a @send@ method. +** @(CHA-ER3a)@ @[Testable]@ Reactions are sent on the channel using a message in "this format":#realtime-room-reactions. +** @(CHA-ER3b)@ @[Testable]@ @metadata@ must not contain the key @ably-chat@. This is reserved for future internal use. If this key is present, the @send@ call shall terminate by throwing an @ErrorInfo@ with code @40001@. +** @(CHA-ER3c)@ @[Testable]@ @headers@ must not contain a key prefixed with @ably-chat@. This is reserved for future internal use. If this key is present, the send call shall terminate by throwing an @ErrorInfo@ with code @40001@. +* @(CHA-ER4)@ A user may subscribe to reaction events in Realtime. +** @(CHA-ER4a)@ @[Testable]@ A user may provide a listener to subscribe to reaction events. This operation must have no side-effects in relation to room or underlying status. When a "realtime message":#realtime-room-reactions with name @roomReaction@ is received, this message is converted into a "reaction object":#chat-structs-ephemeral-reactions and emitted to subscribers. +** @(CHA-ER4b)@ @[Testable]@ A user may unsubscribe a registered listener. This operation must have no side-effects in relation to room or underlying status. Once unsubscribed, subsequent reaction events must not be emitted to this listener. +** @(CHA-ER4c)@ @[Testable]@ Realtime events with an unknown @name@ shall be silently discarded. +** @(CHA-ER4d)@ @[Testable]@ Realtime events that are malformed (unknown fields should be ignored) shall not be emitted to listeners. +* @(CHA-ER5)@ @[Testable]@ Users may subscribe to discontinuity events to know when there's been a break in reactions that they need to resolve. Their listener will be called when a discontinuity event is triggered from the room lifecycle. + +h2(#presence). Online Status (Presence) + +Presence allows chat room users to indicate to others that they're online, as well as other information including their profile picture URL etc. + +@Presence@ shall be exposed to consumers via the @presence@ property of a @Room@. + +* @(CHA-PR1)@ Presence for a Room is exposed on the realtime channel used for chat messages, in the format @::$chat::$chatMessages@. For example, if your room id is @my-room@ then the presence channel will be @my-room::$chat::$chatMessages@. +* @(CHA-PR2)@ The presence payload for Chat is opinionated, so that we may add or change fields later without needing to consider how customers are using presence. +** @(CHA-PR2a)@ @[Testable]@ The presence data format is a JSON object as described below. Customers may specify content of an arbitrary type to be placed in the @userCustomData@ field. + +
+  {
+    "userCustomData": {
+      // Some user specified type, does not have to be object.
+    }
+  }
+
+ +* @(CHA-PR3)@ Users may enter presence. +** @(CHA-PR3a)@ @[Testable]@ Users may choose to enter presence, optionally providing custom data to enter with. The overall presence data must retain the format specified in @CHA-PR2@. +** @(CHA-PR3b)@ @[Testable]@ If the underlying realtime channel is not attached at the time of the @enter@ call, then the @enter@ call must not call @enter@ on the realtime channel, as this triggers an implicit attach. The call must either be delayed until such a time that the channel is already attached. +* @(CHA-PR3)@ Users may update their presence data. +** @(CHA-PR3a)@ @[Testable]@ Users may choose to update their presence data, optionally providing custom data to update with. The overall presence data must retain the format specified in @CHA-PR2@. +** @(CHA-PR3b)@ @[Testable]@ If the underlying realtime channel is not attached at the time of the @update@ call, then the @update@ call must not call @update@ on the realtime channel, as this triggers an implicit attach. The call must either be delayed until such a time that the channel is already attached. +* @(CHA-PR4)@ Users may leave presence. +** @(CHA-PR4a)@ @[Testable]@ Users may choose to leave presence, which results in them being removed from the Realtime presence set. +* @(CHA-PR5)@ @[Testable]@ It must be possible to query if a given clientId is in the presence set. +* @(CHA-PR6)@ @[Testable]@ It must be possible to retrieve all the "@Members":#chat-structs-presence-member of the presence set. +** @(CHA-PR6a)@ @[Testable] As the underlying @presence.get@ operation performs an implicit attach, this operation must not return or call @presence.get@ until the underlying channel is in the attached state. +* @(CHA-PR7)@ Users may subscribe to presence events. +** @(CHA-PR7a)@ @[Testable]@ Users may provide a listener to subscribe to all "presence events":#chat-structs-presence-event in a room. +** @(CHA-PR7b)@ @[Testable]@ Users may provide a listener and a list of selected "presence events":#chat-structs-presence-event, to subscribe to just those events in a room. +** @(CHA-PR7b)@ @[Testable]@ A subscription to presence may be removed, after which it shall receive no further events. +* @(CHA-PR8)@ @[Testable]@ Users may subscribe to discontinuity events to know when there's been a break in presence. Their listener will be called when a discontinuity event is triggered from the room lifecycle. For presence, there shouldn't need to be user action as the underlying core SDK will heal the presence set. + +h2(#rest-api). Chat HTTP REST API + +h3(#rest-general). General + +* @(CHA-RST1)@ REST API requests shall be made via the @request()@ method on the underling Ably SDK. +* @(CHA-RST2)@ REST API requests shall use the API Protocol Version of the underlying core SDK. +* @(CHA-RST3)@ @[Testable]@ REST API requests must be supported using @JSON@ and @msgpack@ as a @Content-Type@ (@useBinaryProtocol@ on the underlying Ably SDK) + +h3(#rest-sending-messages). Sending Messages + +h4(#rest-sending-messages-request). Request + +Below is the full REST payload format. The @metadata@ and @headers@ keys are optional. + +
+  POST /chat/v1/rooms//messages
+  {
+    "text": "the message text",
+    "metadata": {
+      "foo": {
+        "bar": 1
+      }
+    },
+    "headers": {
+      "baz": "qux"
+    }
+  }
+
+ +h4(#rest-sending-messages-request). Response + +A successful request shall result in status code @201 Created@. + +The response body is as follows. + +
+  {
+    "timeserial": "cbfqxperABgItU52203559@1726232498871-0",
+    "createdAt": 1726232498871
+  }
+
+ +h4(#rest-sending-messages-request). Corresponding Realtime Event + +
+  {
+    "name": "message.created"
+    "encoding": "json"
+    "data": {
+      "text": "the message text",
+      "metadata": {
+        "foo": {
+          "bar": 1
+        }
+      }
+    },
+    "timestamp": "1726232498871",
+    "extras": {
+      "headers": {
+        "baz": "qux"
+      },
+      "timeserial": "cbfqxperABgItU52203559@1726232498871-0"
+    }
+  }
+
+ +h3(#rest-fetching-messages). Fetching Message History + +h4(#rest-fetching-messages-request). Request + +
+  GET /chat/v1/rooms//messages
+
+ +The method accepts query parameters identical to the standard Ably REST API. + +h4(#rest-fetching-messages-response). Response + +An array of "@Message@ structs":#chat-structs-message + +h2(#realtime-api). Chat Realtime API + +This section describes the message formats for chat events that occur over a Realtime connection. + +h3(#realtime-room-reactions). Ephemeral Room Reactions + +
+  {
+    "name": "roomReaction"
+    "encoding": "json"
+    "data": {
+      "type": ":heart:",
+      "metadata": {
+        "foo": {
+          "bar": 1
+        }
+      }
+    },
+    "timestamp": "1726232498871", // Only on incoming messages
+    "extras": {
+      "headers": {
+        "baz": "qux"
+      },
+    }
+  }
+
+ + +h2(#chat-structs). Chat Structs and Types + +This section contains an overview of the key types in Chat. This is not intended to be prescriptive to implementation and different ecosystems may expose the underlying properties +in whichever format is most idiomatic to the platform. + +h3(#chat-structs-message). Messages + +
+  {
+    "timeserial": "cbfqxperABgItU52203559@1726232498871-0",
+    "roomId": "my-room",
+    "clientId": "who-sent-the-message",
+    "text": "my-message",
+    "createdAt": DateTime(),
+    "metadata": {
+      "foo": {
+        "bar": 1
+      }
+    },
+    "headers": {
+      "baz": "qux"
+    }
+  }
+
+ +Determining the global order of messages may be achieved by comparing the timeserials. See @CHA-M2@ for more information. + +h3(#chat-structs-ephemeral-reactions). Ephemeral Room Reactions + +
+  {
+    "type": ":heart:",
+    "roomId": "my-room",
+    "clientId": "who-sent-the-message",
+    "createdAt": DateTime(),
+    "metadata": {
+      "foo": {
+        "bar": 1
+      }
+    },
+    "headers": {
+      "baz": "qux"
+    }
+  }
+
+ +h3(#chat-structs-presence-member). Presence Member + +
+  {
+    "clientId": "who-sent-the-message",
+    "action": "enter",
+    "updatedAt": DateTime(),
+    "data": {}, // Whatever the user-provided data is. It must be destructured from the internal format we use.
+    "extras": {
+      "headers": {
+        "baz": "qux"
+      }
+    }
+  }
+
+ +h3(#chat-structs-presence-event). Presence Event + +
+  {
+    "clientId": "who-is-in-presence",
+    "action": "enter",
+    "timestamp": DateTime(),
+    "data": {}, // Whatever the user-provided data is. It must be destructured from the internal format we use.
+  }
+
+ +h2(#error-codes). Chat-specific Error Codes + +This section contains error codes that are specific to Chat. If a specific error code is not listed for a given circumstance, the most appropriate general error code shall be used. For example @400xx@ for client errors or @500xx@ for server errors. + +
+  // The messages feature failed to attach.
+  MessagesAttachmentFailed = 102001
+
+  // The presence feature failed to attach.
+  PresenceAttachmentFailed = 102002
+
+  // The reactions feature failed to attach.
+  ReactionsAttachmentFailed = 102003
+
+  // The occupancy feature failed to attach.
+  OccupancyAttachmentFailed = 102004
+
+  // The typing feature failed to attach.
+  TypingAttachmentFailed = 102005
+  // 102006 - 102049 reserved for future use for attachment errors
+
+  // The messages feature failed to detach.
+  MessagesDetachmentFailed = 102050
+
+  // The presence feature failed to detach.
+  PresenceDetachmentFailed = 102051
+
+  // The reactions feature failed to detach.
+  ReactionsDetachmentFailed = 102052
+
+  // The occupancy feature failed to detach.
+  OccupancyDetachmentFailed = 102053
+
+  // The typing feature failed to detach.
+  TypingDetachmentFailed = 102054
+  // 102055 - 102099 reserved for future use for detachment errors
+
+  // The room has experienced a discontinuity.
+  RoomDiscontinuity = 102100
+
+  // Unable to perform operation;
+
+  // Cannot perform operation because the room is in a failed state.
+  RoomInFailedState = 102101
+
+  // Cannot perform operation because the room is in a releasing state.
+  RoomIsReleasing = 102102
+
+  // Cannot perform operation because the room is in a released state.
+  RoomIsReleased = 102103
+
+  // Cannot perform operation because the previous operation failed.
+  PreviousOperationFailed = 102104
+
+  // An unknown error has happened in the room lifecycle.
+  RoomLifecycleError = 102105
+
diff --git a/textile/chat-protocol.textile b/textile/chat-protocol.textile deleted file mode 100644 index 2e62e618..00000000 --- a/textile/chat-protocol.textile +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Chat Protocol -section: client-lib-development-guide -index: 50 -jump_to: - Help with: - - Chat Protocol Overview#overview ---- - -h2(#overview). Overview - -Section dedicated to new chat protocol development. TBC