Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 46
[ECO-5517] Implement getRoot implicit attach#1146
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
a02b31a0ac1e1a523dfe4a035ee7File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -5,6 +5,7 @@ import io.ably.lib.realtime.CompletionListener | ||
| import io.ably.lib.types.ChannelMode | ||
| import io.ably.lib.types.ErrorInfo | ||
| import io.ably.lib.types.ProtocolMessage | ||
| import kotlinx.coroutines.CompletableDeferred | ||
| import kotlinx.coroutines.suspendCancellableCoroutine | ||
| import kotlin.coroutines.resume | ||
| import kotlin.coroutines.resumeWithException | ||
| @@ -14,7 +15,7 @@ import kotlin.coroutines.resumeWithException | ||
| */ | ||
| internal suspend fun LiveObjectsAdapter.sendAsync(message: ProtocolMessage) = suspendCancellableCoroutine { continuation -> | ||
| try { | ||
| this.send(message, object : CompletionListener { | ||
| connectionManager.send(message, clientOptions.queueMessages, object : CompletionListener { | ||
| override fun onSuccess() { | ||
| continuation.resume(Unit) | ||
| } | ||
| @@ -28,11 +29,54 @@ internal suspend fun LiveObjectsAdapter.sendAsync(message: ProtocolMessage) = su | ||
| } | ||
| } | ||
| internal suspend fun LiveObjectsAdapter.attachAsync(channelName: String) = suspendCancellableCoroutine { continuation -> | ||
| try { | ||
| getChannel(channelName).attach(object : CompletionListener { | ||
| override fun onSuccess() { | ||
| continuation.resume(Unit) | ||
| } | ||
| override fun onError(reason: ErrorInfo) { | ||
| continuation.resumeWithException(ablyException(reason)) | ||
| } | ||
| }) | ||
| } catch (e: Exception) { | ||
| continuation.resumeWithException(e) | ||
| } | ||
| } | ||
sacOO7 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| /** | ||
| * Retrieves the channel modes for a specific channel. | ||
| * This method returns the modes that are set for the specified channel. | ||
| * | ||
| * @param channelName the name of the channel for which to retrieve the modes | ||
| * @return the array of channel modes for the specified channel, or null if the channel is not found | ||
| * Spec: RTO2a, RTO2b | ||
| */ | ||
| internal fun LiveObjectsAdapter.getChannelModes(channelName: String): Array<ChannelMode>? { | ||
| val channel = getChannel(channelName) | ||
| // RTO2a - channel.modes is only populated on channel attachment, so use it only if it is set | ||
| channel.modes?.let { modes -> | ||
| if (modes.isNotEmpty()) { | ||
| return modes | ||
| } | ||
| } | ||
| // RTO2b - otherwise as a best effort use user provided channel options | ||
| channel.options?.let { options -> | ||
| if (options.hasModes()) { | ||
| return options.modes | ||
| } | ||
| } | ||
| return null | ||
| } | ||
| /** | ||
| * Spec: RTO15d | ||
| */ | ||
| internal fun LiveObjectsAdapter.ensureMessageSizeWithinLimit(objectMessages: Array<ObjectMessage>) { | ||
| val maximumAllowedSize = maxMessageSizeLimit() | ||
| val maximumAllowedSize = connectionManager.maxMessageSize | ||
| val objectsTotalMessageSize = objectMessages.sumOf { it.size() } | ||
| if (objectsTotalMessageSize > maximumAllowedSize) { | ||
| throw ablyException("ObjectMessages size $objectsTotalMessageSize exceeds maximum allowed size of $maximumAllowedSize bytes", | ||
| @@ -44,19 +88,46 @@ internal fun LiveObjectsAdapter.setChannelSerial(channelName: String, protocolMe | ||
| if (protocolMessage.action != ProtocolMessage.Action.`object`) return | ||
| val channelSerial = protocolMessage.channelSerial | ||
| if (channelSerial.isNullOrEmpty()) return | ||
| setChannelSerial(channelName, channelSerial) | ||
| getChannel(channelName).properties.channelSerial = channelSerial | ||
| } | ||
| internal suspend fun LiveObjectsAdapter.ensureAttached(channelName: String) { | ||
| val channel = getChannel(channelName) | ||
| when (val currentChannelStatus = channel.state) { | ||
| ChannelState.initialized -> attachAsync(channelName) | ||
| ChannelState.attached -> return | ||
| ChannelState.attaching -> { | ||
| val attachDeferred = CompletableDeferred<Unit>() | ||
| getChannel(channelName).once { | ||
| when(it.current) { | ||
| ChannelState.attached -> attachDeferred.complete(Unit) | ||
| else -> { | ||
| val exception = ablyException("Channel $channelName is in invalid state: ${it.current}, " + | ||
| "error: ${it.reason}", ErrorCode.ChannelStateError) | ||
| attachDeferred.completeExceptionally(exception) | ||
| } | ||
| } | ||
| } | ||
| if (channel.state == ChannelState.attached) { | ||
| attachDeferred.complete(Unit) | ||
| } | ||
sacOO7 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| attachDeferred.await() | ||
| } | ||
| else -> | ||
| throw ablyException("Channel $channelName is in invalid state: $currentChannelStatus", ErrorCode.ChannelStateError) | ||
| } | ||
| } | ||
| // Spec: RTLO4b1, RTLO4b2 | ||
| internal fun LiveObjectsAdapter.throwIfInvalidAccessApiConfiguration(channelName: String) { | ||
| throwIfMissingChannelMode(channelName, ChannelMode.object_subscribe) | ||
| throwIfInChannelState(channelName, arrayOf(ChannelState.detached, ChannelState.failed)) | ||
| throwIfMissingChannelMode(channelName, ChannelMode.object_subscribe) | ||
| } | ||
| internal fun LiveObjectsAdapter.throwIfInvalidWriteApiConfiguration(channelName: String) { | ||
| throwIfEchoMessagesDisabled() | ||
| throwIfMissingChannelMode(channelName, ChannelMode.object_publish) | ||
| throwIfInChannelState(channelName, arrayOf(ChannelState.detached, ChannelState.failed, ChannelState.suspended)) | ||
| throwIfMissingChannelMode(channelName, ChannelMode.object_publish) | ||
| } | ||
| internal fun LiveObjectsAdapter.throwIfUnpublishableState(channelName: String) { | ||
| @@ -67,16 +138,16 @@ internal fun LiveObjectsAdapter.throwIfUnpublishableState(channelName: String) { | ||
| } | ||
| // Spec: RTO2 | ||
| internal fun LiveObjectsAdapter.throwIfMissingChannelMode(channelName: String, channelMode: ChannelMode) { | ||
| private fun LiveObjectsAdapter.throwIfMissingChannelMode(channelName: String, channelMode: ChannelMode) { | ||
| val channelModes = getChannelModes(channelName) | ||
| if (channelModes == null || !channelModes.contains(channelMode)) { | ||
| // Spec: RTO2a2, RTO2b2 | ||
| throw ablyException("\"${channelMode.name}\" channel mode must be set for this operation", ErrorCode.ChannelModeRequired) | ||
| } | ||
| } | ||
| internal fun LiveObjectsAdapter.throwIfInChannelState(channelName: String, channelStates: Array<ChannelState>) { | ||
| val currentState = getChannelState(channelName) | ||
| private fun LiveObjectsAdapter.throwIfInChannelState(channelName: String, channelStates: Array<ChannelState>) { | ||
| val currentState = getChannel(channelName).state | ||
| if (currentState == null || channelStates.contains(currentState)) { | ||
| throw ablyException("Channel is in invalid state: $currentState", ErrorCode.ChannelStateError) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -29,14 +29,13 @@ abstract class IntegrationTest { | ||
| /** | ||
| * Retrieves a realtime channel for the specified channel name and client ID | ||
| * If a client with the given clientID does not exist, a new client is created using the provided options. | ||
| * The channel is attached and ensured to be in the attached state before returning. | ||
| * | ||
| * @param channelName Name of the channel | ||
| * @param clientId The ID of the client to use or create. Defaults to "client1". | ||
| * @return The attached realtime channel. | ||
| * @throws Exception If the channel fails to attach or the client fails to connect. | ||
| * @return The realtime channel in the INITIALIZED state. | ||
| * @throws Exception If the client fails to connect. | ||
| */ | ||
| internal suspend fun getRealtimeChannel(channelName: String, clientId: String = "client1", autoAttach: Boolean = true): Channel { | ||
| internal suspend fun getRealtimeChannel(channelName: String, clientId: String = "client1"): Channel { | ||
sacOO7 marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| val client = realtimeClients.getOrPut(clientId) { | ||
| sandbox.createRealtimeClient { | ||
| this.clientId = clientId | ||
| @@ -46,12 +45,7 @@ abstract class IntegrationTest { | ||
| val channelOpts = ChannelOptions().apply { | ||
| modes = arrayOf(ChannelMode.object_publish, ChannelMode.object_subscribe) | ||
| } | ||
| return client.channels.get(channelName, channelOpts).apply { | ||
| if (autoAttach) { | ||
| attach() | ||
| ensureAttached() | ||
| } | ||
| } | ||
| return client.channels.get(channelName, channelOpts) | ||
| } | ||
| /** | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.