Add the SuperTokens lazy migration flow#74
Conversation
Reviewer's GuideImplements a lazy SuperTokens user migration flow in the Android SDK by introducing SuperTokens configuration into RowndConfig, wiring a sign-in completion event listener in RowndClient, and adding a utility that POSTs the current Rownd access token to the SuperTokens migration endpoint for new users. Sequence diagram for SuperTokens lazy migration on sign-in completionsequenceDiagram
actor User
participant App
participant RowndClient
participant EventEmitter
participant Store
participant SuperTokensSync
participant SuperTokensAPI
User->>App: Trigger sign in
App->>RowndClient: startSignIn()
RowndClient->>EventEmitter: emit RowndEvent(SignInCompleted, data)
EventEmitter->>RowndClient: invoke listener(event)
alt event.event == SignInCompleted and user_type == new_user
RowndClient->>Store: get currentState.auth.accessToken
Store-->>RowndClient: accessToken
RowndClient->>RowndClient: read config.supertokens.appInfo
RowndClient->>SuperTokensSync: launch coroutine syncUserToSuperTokens(accessToken, appInfo)
activate SuperTokensSync
SuperTokensSync->>SuperTokensAPI: POST /auth/plugin/rownd/migrate
SuperTokensAPI-->>SuperTokensSync: 2xx or error status
SuperTokensSync-->>RowndClient: return
deactivate SuperTokensSync
else not new_user or missing config/token
EventEmitter-->>RowndClient: listener returns without sync
end
Class diagram for SuperTokens configuration and sync utilityclassDiagram
class RowndConfig {
String appKey
String? apiUrl
Boolean debug
Boolean enableSdkTelemetry
Boolean enableSmartLinkPasteBehavior
SuperTokensConfig? supertokens
}
class SuperTokensConfig {
SuperTokensAppInfo appInfo
}
class SuperTokensAppInfo {
String appName
String apiDomain
String apiBasePath = "/auth"
}
class RowndClient {
RowndConfig config
Store store
EventEmitter eventEmitter
Telemetry telemetry
initRownd()
}
class SuperTokensSync {
<<utility>>
+suspend syncUserToSuperTokens(accessToken: String, appInfo: SuperTokensAppInfo)
}
RowndConfig --> SuperTokensConfig : has optional
SuperTokensConfig --> SuperTokensAppInfo : has
RowndClient --> RowndConfig : uses
RowndClient ..> SuperTokensSync : calls
SuperTokensSync --> SuperTokensAppInfo : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Review Summary by QodoAdd SuperTokens lazy migration flow to Android SDK
WalkthroughsDescription• Adds SuperTokens lazy user migration flow to Android SDK • Implements event listener for sign-in completion events • Creates SuperTokens configuration models for app integration • Calls migration endpoint when new user signs in Diagramflowchart LR
A["Sign-in Completed"] -->|"new_user event"| B["Event Listener"]
B -->|"Extract access token"| C["Rownd State"]
C -->|"POST to migrate endpoint"| D["SuperTokens API"]
D -->|"User migrated"| E["Sync Complete"]
File Changes1. android/src/main/java/io/rownd/android/Rownd.kt
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The
RowndClientevent listener creates a newCoroutineScope(Dispatchers.IO)for each sign-in; consider using an existing structured coroutine scope (e.g., tied torowndContextor a lifecycle) to avoid unmanaged coroutines and potential leaks. - In
syncUserToSuperTokens,HttpURLConnectionis used without configuring connection/read timeouts or explicitly closing streams, which can lead to hanging requests or resource leaks; set reasonable timeouts and ensure any streams are closed. - The construction of the SuperTokens base URL (
"${appInfo.apiDomain}${appInfo.apiBasePath}") assumes the domain and path are already well-formed; consider normalizing or validating these values (e.g., ensuring scheme and leading/trailing slashes) to avoid subtle URL issues across different configurations.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `RowndClient` event listener creates a new `CoroutineScope(Dispatchers.IO)` for each sign-in; consider using an existing structured coroutine scope (e.g., tied to `rowndContext` or a lifecycle) to avoid unmanaged coroutines and potential leaks.
- In `syncUserToSuperTokens`, `HttpURLConnection` is used without configuring connection/read timeouts or explicitly closing streams, which can lead to hanging requests or resource leaks; set reasonable timeouts and ensure any streams are closed.
- The construction of the SuperTokens base URL (`"${appInfo.apiDomain}${appInfo.apiBasePath}"`) assumes the domain and path are already well-formed; consider normalizing or validating these values (e.g., ensuring scheme and leading/trailing slashes) to avoid subtle URL issues across different configurations.
## Individual Comments
### Comment 1
<location path="android/src/main/java/io/rownd/android/util/SuperTokensSync.kt" line_range="17" />
<code_context>
+ val base = "${appInfo.apiDomain}${appInfo.apiBasePath}"
+
+ try {
+ val conn = URL("$base/plugin/rownd/migrate").openConnection() as HttpURLConnection
+ conn.requestMethod = "POST"
+ conn.setRequestProperty("Authorization", "Bearer $accessToken")
</code_context>
<issue_to_address>
**suggestion (performance):** Consider configuring connect and read timeouts on the HttpURLConnection.
Relying on default timeouts can cause this call to hang for a long time under poor network conditions. Please set explicit `connectTimeout` and `readTimeout` values so this background sync fails or recovers in a predictable timeframe.
Suggested implementation:
```
try {
val conn = URL("$base/plugin/rownd/migrate").openConnection() as HttpURLConnection
conn.connectTimeout = 5_000 // 5 seconds
conn.readTimeout = 10_000 // 10 seconds
conn.requestMethod = "POST"
conn.setRequestProperty("Authorization", "Bearer $accessToken")
```
If there is an existing configuration or constants file for network settings in this project, you may want to:
1. Extract the `5_000` and `10_000` values into named constants (e.g., `DEFAULT_CONNECT_TIMEOUT_MS`, `DEFAULT_READ_TIMEOUT_MS`) in that shared location.
2. Replace the inline literals here with those constants to keep timeout behavior consistent across the codebase.
</issue_to_address>
### Comment 2
<location path="android/src/main/java/io/rownd/android/util/SuperTokensSync.kt" line_range="16-27" />
<code_context>
+ val conn = URL("$base/plugin/rownd/migrate").openConnection() as HttpURLConnection
+ conn.requestMethod = "POST"
+ conn.setRequestProperty("Authorization", "Bearer $accessToken")
+ val code = conn.responseCode
+ if (code !in 200..299) {
+ Log.e("Rownd.ST", "[Rownd->ST] migrate failed with status: $code")
</code_context>
<issue_to_address>
**suggestion (performance):** Consider consuming and closing the response body to ensure proper connection reuse.
Relying only on `responseCode` may leave the body unread, which can interfere with HTTP connection pooling. Read and close the `inputStream` or `errorStream` (even if you discard the data) before disconnecting to maximize connection reuse.
```suggestion
try {
val conn = URL("$base/plugin/rownd/migrate").openConnection() as HttpURLConnection
conn.requestMethod = "POST"
conn.setRequestProperty("Authorization", "Bearer $accessToken")
val code = conn.responseCode
// Consume and close the response body (success or error) to enable connection reuse
val responseStream = try {
if (code in 200..299) conn.inputStream else conn.errorStream
} catch (_: Exception) {
null
}
responseStream?.use { stream ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
while (stream.read(buffer) != -1) {
// Discard response body
}
}
if (code !in 200..299) {
Log.e("Rownd.ST", "[Rownd->ST] migrate failed with status: $code")
}
conn.disconnect()
} catch (e: Exception) {
Log.e("Rownd.ST", "[Rownd->ST] migrate failed (non-fatal): ${e.message}")
}
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
6416449 to
4473272
Compare
Summary
Adds SuperTokens lazy user migration support to the Android SDK.
When the SuperTokens config is passed aand a Rownd sign-in completes for a
new_user, the SDK now calls the${apiBasePath}/plugin/rownd/migrateendpoint.Sync flow:
Rownd.config.supertokenswith the SuperTokens app info.Rowndregisters an event listener forSignInCompleted.new_user, the SDK reads the current Rownd access token from state.Summary by Sourcery
Add support for lazily migrating newly created Rownd users to SuperTokens when configured.
New Features:
Enhancements: