Skip to content
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

[ECO-5009][CHA-RL1] Room ATTACH #27

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6d2d24c
Added checks for current room state before room ATTACH operation
sacOO7 Oct 3, 2024
d018b0d
Implemented RoomStatus interface, added code to handle exceptions when
sacOO7 Oct 8, 2024
99c7363
Marked RoomOptions as optional as per spec
sacOO7 Oct 11, 2024
3067774
Added offAll method interface method to remove all listeners
sacOO7 Oct 11, 2024
8ffabb1
Refactored room interfaces, implemented DefaultRoomStatus class that
sacOO7 Oct 11, 2024
5b321c6
Replaced ChatEventEmitter with RoomStatusEventEmitter, implemented mi…
sacOO7 Oct 14, 2024
7943aba
Added proper logging for DefaultRoom room attach operation failure
sacOO7 Oct 15, 2024
21ac68a
Added RoomLifecycleManager class, defined basic interfaces needed for
sacOO7 Oct 15, 2024
0d7f9a9
Updated coroutine core as a direct dependency to chat-android package
sacOO7 Oct 16, 2024
ba0f81e
Implemented missing interfaces as a part of RoomLifeCycleManager
sacOO7 Oct 16, 2024
5daeaf3
Merge branch 'main' into feature/room-ATTACH
sacOO7 Oct 18, 2024
899363b
Fixed linting issues recommended by detekt
sacOO7 Oct 18, 2024
102513c
Updated ably-java dependency to latest version
sacOO7 Oct 21, 2024
572fd8d
Updated ContributesToRoomLifecycle channel property to CompletableDef…
sacOO7 Oct 21, 2024
da00860
Upgraded kotlinx.coroutines dependency to latest version
sacOO7 Oct 21, 2024
be11428
Renamed DefaultRoomStatusStatus class to DefaultStatus, same as chat-js
sacOO7 Oct 21, 2024
a68a2a7
Implemented basic definition for RoomLifeCycleManager
sacOO7 Oct 21, 2024
a5e837c
Added interface impl. for ContributesToRoomLifecycle and ResolvedCont…
sacOO7 Oct 21, 2024
5dfb474
Fixed linting errors in local files
sacOO7 Oct 21, 2024
e0d5dab
Made AblyRealtimeChannel as a explicit type for all room feature chan…
sacOO7 Oct 22, 2024
0460c5c
Added TODOs before initializing RoomLifeCycleManager, refactored room
sacOO7 Oct 22, 2024
b5a5e54
Removed unused coroutinescope from the RoomLifeCycleManager, added mi…
sacOO7 Oct 22, 2024
ce9c0ba
Fixed RoomStatusEventEmitter typo,
sacOO7 Nov 8, 2024
d689419
Merge branch 'main' into feature/room-ATTACH
sacOO7 Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions chat-android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ buildConfig {
dependencies {
api(libs.ably.android)
implementation(libs.gson)
implementation(libs.coroutine.core)

testImplementation(libs.junit)
testImplementation(libs.mockk)
Expand Down
4 changes: 2 additions & 2 deletions chat-android/src/main/java/com/ably/chat/ChatApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ internal class ChatApi(private val realtimeClient: RealtimeClient, private val c
ErrorInfo(
"Metadata contains reserved 'ably-chat' key",
HttpStatusCodes.BadRequest,
ErrorCodes.InvalidRequestBody,
ErrorCodes.InvalidRequestBody.errorCode,
),
)
}
Expand All @@ -98,7 +98,7 @@ internal class ChatApi(private val realtimeClient: RealtimeClient, private val c
ErrorInfo(
"Headers contains reserved key with reserved 'ably-chat' prefix",
HttpStatusCodes.BadRequest,
ErrorCodes.InvalidRequestBody,
ErrorCodes.InvalidRequestBody.errorCode,
),
)
}
Expand Down
45 changes: 23 additions & 22 deletions chat-android/src/main/java/com/ably/chat/ErrorCodes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,105 +3,106 @@ package com.ably.chat
/**
* Error codes for the Chat SDK.
*/
object ErrorCodes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do you think enum here is better?

Copy link
Author

@sacOO7 sacOO7 Nov 8, 2024

Choose a reason for hiding this comment

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

Having type for given ErrorCode enforces compile time check for right assertions with reusability.
Also, IDE gives proper suggestions when matching error codes, unlike when it's generic Int.
This makes it easy to write ErrorCode assertions in code as well as tests.

Copy link
Author

Choose a reason for hiding this comment

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

Also, in chat-js, ErrorCode is represented as an enum, this makes it super easy to correlate between two codebases.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure I understand, could you give an example? Typescript's enums are nothing like Kotlin's enums. Error codes usually come from backend, and they come as Int. I currently don't understand what benefits we get, except we have to additionally serialize/deserialize Int into enum

Copy link
Author

Choose a reason for hiding this comment

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

Sure, I am closing this PR in favor of #33. We can continue discussion for the same there 👍

enum class ErrorCodes(val errorCode: Int) {

/**
* The messages feature failed to attach.
*/
const val MessagesAttachmentFailed = 102_001
MessagesAttachmentFailed(102_001),

/**
* The presence feature failed to attach.
*/
const val PresenceAttachmentFailed = 102_002
PresenceAttachmentFailed(102_002),

/**
* The reactions feature failed to attach.
*/
const val ReactionsAttachmentFailed = 102_003
ReactionsAttachmentFailed(102_003),

/**
* The occupancy feature failed to attach.
*/
const val OccupancyAttachmentFailed = 102_004
OccupancyAttachmentFailed(102_004),

/**
* The typing feature failed to attach.
*/
const val TypingAttachmentFailed = 102_005
// 102006 - 102049 reserved for future use for attachment errors
TypingAttachmentFailed(102_005),
// 102_006 - 102_049 reserved for future use for attachment errors

/**
* The messages feature failed to detach.
*/
const val MessagesDetachmentFailed = 102_050
MessagesDetachmentFailed(102_050),

/**
* The presence feature failed to detach.
*/
const val PresenceDetachmentFailed = 102_051
PresenceDetachmentFailed(102_051),

/**
* The reactions feature failed to detach.
*/
const val ReactionsDetachmentFailed = 102_052
ReactionsDetachmentFailed(102_052),

/**
* The occupancy feature failed to detach.
*/
const val OccupancyDetachmentFailed = 102_053
OccupancyDetachmentFailed(102_053),

/**
* The typing feature failed to detach.
*/
const val TypingDetachmentFailed = 102_054
// 102055 - 102099 reserved for future use for detachment errors
TypingDetachmentFailed(102_054),
// 102_055 - 102_099 reserved for future use for detachment errors

/**
* The room has experienced a discontinuity.
*/
const val RoomDiscontinuity = 102_100
RoomDiscontinuity(102_100),

// Unable to perform operation;

/**
* Cannot perform operation because the room is in a failed state.
*/
const val RoomInFailedState = 102_101
RoomInFailedState(102_101),

/**
* Cannot perform operation because the room is in a releasing state.
*/
const val RoomIsReleasing = 102_102
RoomIsReleasing(102_102),

/**
* Cannot perform operation because the room is in a released state.
*/
const val RoomIsReleased = 102_103
RoomIsReleased(102_103),

/**
* Cannot perform operation because the previous operation failed.
*/
const val PreviousOperationFailed = 102_104
PreviousOperationFailed(102_104),

/**
* An unknown error has happened in the room lifecycle.
*/
const val RoomLifecycleError = 102_105
RoomLifecycleError(102_105),

/**
* The request cannot be understood
*/
const val BadRequest = 40_000
BadRequest(40_000),

/**
* Invalid request body
*/
const val InvalidRequestBody = 40_001
InvalidRequestBody(40_001),

/**
* Internal error
*/
const val InternalError = 50_000
InternalError(50_000),
}

/**
Expand Down
34 changes: 24 additions & 10 deletions chat-android/src/main/java/com/ably/chat/Messages.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ package com.ably.chat
import com.ably.chat.QueryOptions.MessageOrder.NewestFirst
import com.google.gson.JsonObject
import io.ably.lib.realtime.AblyRealtime
import io.ably.lib.realtime.Channel
import io.ably.lib.realtime.ChannelState
import io.ably.lib.realtime.ChannelStateListener
import io.ably.lib.types.AblyException
import io.ably.lib.types.ErrorInfo
import io.ably.lib.realtime.Channel as AblyRealtimeChannel

typealias PubSubMessageListener = Channel.MessageListener
typealias PubSubMessageListener = AblyRealtimeChannel.MessageListener
typealias PubSubMessage = io.ably.lib.types.Message

/**
Expand All @@ -26,7 +26,7 @@ interface Messages : EmitsDiscontinuities {
*
* @returns the realtime channel
*/
val channel: Channel
val channel: AblyRealtimeChannel

/**
* Subscribe to new messages in this chat room.
Expand Down Expand Up @@ -207,7 +207,7 @@ internal class DefaultMessagesSubscription(
ErrorInfo(
"The `end` parameter is specified and is more recent than the subscription point timeserial",
HttpStatusCodes.BadRequest,
ErrorCodes.BadRequest,
ErrorCodes.BadRequest.errorCode,
),
)
}
Expand All @@ -225,7 +225,7 @@ internal class DefaultMessages(
private val roomId: String,
realtimeChannels: AblyRealtime.Channels,
private val chatApi: ChatApi,
) : Messages {
) : Messages, ContributesToRoomLifecycle, ResolvedContributor {

private var listeners: Map<Messages.Listener, DeferredValue<String>> = emptyMap()

Expand All @@ -239,7 +239,13 @@ internal class DefaultMessages(
*/
private val messagesChannelName = "$roomId::\$chat::\$chatMessages"

override val channel: Channel = realtimeChannels.get(messagesChannelName, ChatChannelOptions())
override val channel = realtimeChannels.get(messagesChannelName, ChatChannelOptions())

override val contributor: ContributesToRoomLifecycle = this

override val attachmentErrorCode: ErrorCodes = ErrorCodes.MessagesAttachmentFailed

override val detachmentErrorCode: ErrorCodes = ErrorCodes.MessagesDetachmentFailed

init {
channelStateListener = ChannelStateListener {
Expand All @@ -253,7 +259,7 @@ internal class DefaultMessages(
addListener(listener, deferredChannelSerial)
val messageListener = PubSubMessageListener {
val pubSubMessage = it ?: throw AblyException.fromErrorInfo(
ErrorInfo("Got empty pubsub channel message", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest),
ErrorInfo("Got empty pubsub channel message", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest.errorCode),
)
val data = parsePubSubMessageData(pubSubMessage.data)
val chatMessage = Message(
Expand Down Expand Up @@ -284,7 +290,7 @@ internal class DefaultMessages(
ErrorInfo(
"This messages subscription instance was already unsubscribed",
HttpStatusCodes.BadRequest,
ErrorCodes.BadRequest,
ErrorCodes.BadRequest.errorCode,
),
)
},
Expand Down Expand Up @@ -323,14 +329,22 @@ internal class DefaultMessages(
private fun requireChannelSerial(): String {
return channel.properties.channelSerial
?: throw AblyException.fromErrorInfo(
ErrorInfo("Channel has been attached, but channelSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest),
ErrorInfo(
"Channel has been attached, but channelSerial is not defined",
HttpStatusCodes.BadRequest,
ErrorCodes.BadRequest.errorCode,
),
)
}

private fun requireAttachSerial(): String {
return channel.properties.attachSerial
?: throw AblyException.fromErrorInfo(
ErrorInfo("Channel has been attached, but attachSerial is not defined", HttpStatusCodes.BadRequest, ErrorCodes.BadRequest),
ErrorInfo(
"Channel has been attached, but attachSerial is not defined",
HttpStatusCodes.BadRequest,
ErrorCodes.BadRequest.errorCode,
),
)
}

Expand Down
16 changes: 11 additions & 5 deletions chat-android/src/main/java/com/ably/chat/Occupancy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package com.ably.chat

import io.ably.lib.realtime.Channel
import io.ably.lib.realtime.Channel as AblyRealtimeChannel

/**
* This interface is used to interact with occupancy in a chat room: subscribing to occupancy updates and
Expand All @@ -16,7 +16,7 @@ interface Occupancy : EmitsDiscontinuities {
*
* @returns The underlying Ably channel for occupancy events.
*/
val channel: Channel
val channel: AblyRealtimeChannel

/**
* Subscribe a given listener to occupancy updates of the chat room.
Expand Down Expand Up @@ -61,9 +61,15 @@ data class OccupancyEvent(

internal class DefaultOccupancy(
private val messages: Messages,
) : Occupancy {
override val channel: Channel
get() = messages.channel
) : Occupancy, ContributesToRoomLifecycle, ResolvedContributor {

override val channel = messages.channel

override val contributor: ContributesToRoomLifecycle = this

override val attachmentErrorCode: ErrorCodes = ErrorCodes.OccupancyAttachmentFailed

override val detachmentErrorCode: ErrorCodes = ErrorCodes.OccupancyDetachmentFailed

override fun subscribe(listener: Occupancy.Listener): Subscription {
TODO("Not yet implemented")
Expand Down
15 changes: 10 additions & 5 deletions chat-android/src/main/java/com/ably/chat/Presence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
package com.ably.chat

import android.text.PrecomputedText.Params
import io.ably.lib.realtime.Channel
import io.ably.lib.types.PresenceMessage
import io.ably.lib.realtime.Channel as AblyRealtimeChannel

typealias PresenceData = Any

Expand All @@ -19,7 +19,7 @@ interface Presence : EmitsDiscontinuities {
* Get the underlying Ably realtime channel used for presence in this chat room.
* @returns The realtime channel.
*/
val channel: Channel
val channel: AblyRealtimeChannel

/**
* Method to get list of the current online users and returns the latest presence messages associated to it.
Expand Down Expand Up @@ -131,10 +131,15 @@ data class PresenceEvent(

internal class DefaultPresence(
private val messages: Messages,
) : Presence {
) : Presence, ContributesToRoomLifecycle, ResolvedContributor {

override val channel: Channel
get() = messages.channel
override val channel = messages.channel

override val contributor: ContributesToRoomLifecycle = this

override val attachmentErrorCode: ErrorCodes = ErrorCodes.PresenceAttachmentFailed

override val detachmentErrorCode: ErrorCodes = ErrorCodes.PresenceDetachmentFailed

override suspend fun get(params: List<Params>): List<PresenceMember> {
TODO("Not yet implemented")
Expand Down
Loading
Loading