Skip to content

Commit

Permalink
Merge pull request #216 from stytchauth/jordan/SDK-2090-local-session…
Browse files Browse the repository at this point in the history
…-caching

SDK-2090 Implement local session/user/member/organization caching
  • Loading branch information
jhaven-stytch authored Oct 8, 2024
2 parents 60b7fce + 1f41a83 commit 7e1c1e8
Show file tree
Hide file tree
Showing 60 changed files with 1,037 additions and 477 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import com.stytch.sdk.common.StytchObjectInfo;
import com.stytch.sdk.common.StytchResult;
import com.stytch.sdk.common.network.models.BasicData;
import com.stytch.sdk.consumer.StytchClient;
Expand Down Expand Up @@ -36,7 +37,11 @@ Unit handleInitializationChange(Boolean isInitialized) {
return Unit.INSTANCE;
}

private Unit handleUserChange(UserData userData) {
private Unit handleUserChange(StytchObjectInfo<UserData> stytchUser) {
UserData userData = null;
if (stytchUser instanceof StytchObjectInfo.Available) {
userData = ((StytchObjectInfo.Available<UserData>) stytchUser).getValue();
}
StytchState newState = new StytchState(
true,
StytchClient.getSessions().getSync(),
Expand All @@ -46,7 +51,11 @@ private Unit handleUserChange(UserData userData) {
return Unit.INSTANCE;
}

private Unit handleSessionChange(SessionData sessionData) {
private Unit handleSessionChange(StytchObjectInfo<SessionData> stytchSession) {
SessionData sessionData = null;
if (stytchSession instanceof StytchObjectInfo.Available) {
sessionData = ((StytchObjectInfo.Available<SessionData>) stytchSession).getValue();
}
StytchState newState = new StytchState(
true,
sessionData,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,51 @@ package com.stytch.stytchexampleapp
import android.os.Parcelable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.stytch.sdk.common.StytchObjectInfo
import com.stytch.sdk.consumer.StytchClient
import com.stytch.sdk.consumer.network.models.SessionData
import com.stytch.sdk.consumer.network.models.UserData
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize

class MainViewModel : ViewModel() {
val authenticationState =
combine(
StytchClient.isInitialized,
StytchClient.user.onChange,
StytchClient.sessions.onChange,
) { isInitialized, userData, sessionData ->
AuthenticationState(
isInitialized = isInitialized,
userData = userData,
sessionData = sessionData,
)
}.stateIn(viewModelScope, SharingStarted.Lazily, AuthenticationState())
private var _authenticationState = MutableStateFlow(AuthenticationState())
var authenticationState: StateFlow<AuthenticationState> = _authenticationState.asStateFlow()

init {
viewModelScope.launch {
authenticationState =
combine(
StytchClient.isInitialized,
StytchClient.user.onChange,
StytchClient.sessions.onChange,
) { isInitialized, stytchUser, stytchSession ->
val userData =
if (stytchUser is StytchObjectInfo.Available) {
stytchUser.value
} else {
null
}
val sessionData =
if (stytchSession is StytchObjectInfo.Available) {
stytchSession.value
} else {
null
}
AuthenticationState(
isInitialized = isInitialized,
userData = userData,
sessionData = sessionData,
)
}.stateIn(viewModelScope, SharingStarted.Lazily, AuthenticationState())
}
}
}

@Parcelize
Expand Down
2 changes: 1 addition & 1 deletion source/sdk/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ plugins {

ext {
PUBLISH_GROUP_ID = 'com.stytch.sdk'
PUBLISH_VERSION = '0.28.0'
PUBLISH_VERSION = '0.29.0'
PUBLISH_ARTIFACT_ID = 'sdk'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,12 @@ public object StytchB2BClient {
)
// if there are session identifiers on device start the auto updater to ensure it is still valid
if (sessionStorage.persistedSessionIdentifiersExist) {
StytchB2BApi.Sessions.authenticate(null).apply {
launchSessionUpdater(dispatchers, sessionStorage)
sessionStorage.memberSession?.let {
// if we have a session, it's expiration date has already been validated, now attempt
// to validate it with the Stytch servers
StytchB2BApi.Sessions.authenticate(null).apply {
launchSessionUpdater(dispatchers, sessionStorage)
}
}
}
_isInitialized.value = true
Expand Down
5 changes: 3 additions & 2 deletions source/sdk/src/main/java/com/stytch/sdk/b2b/member/Member.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.stytch.sdk.b2b.MemberResponse
import com.stytch.sdk.b2b.UpdateMemberResponse
import com.stytch.sdk.b2b.network.models.MemberData
import com.stytch.sdk.b2b.network.models.MfaMethod
import com.stytch.sdk.common.StytchObjectInfo
import kotlinx.coroutines.flow.StateFlow
import java.util.concurrent.CompletableFuture

Expand All @@ -15,13 +16,13 @@ public interface Member {
/**
* Exposes a flow of member data
*/
public val onChange: StateFlow<MemberData?>
public val onChange: StateFlow<StytchObjectInfo<MemberData>>

/**
* Assign a callback that will be called when the member data changes
*/

public fun onChange(callback: (MemberData?) -> Unit)
public fun onChange(callback: (StytchObjectInfo<MemberData>) -> Unit)

/**
* Wraps Stytch’s organization/members/me endpoint.
Expand Down
17 changes: 14 additions & 3 deletions source/sdk/src/main/java/com/stytch/sdk/b2b/member/MemberImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@ import com.stytch.sdk.b2b.network.StytchB2BApi
import com.stytch.sdk.b2b.network.models.MemberData
import com.stytch.sdk.b2b.sessions.B2BSessionStorage
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchObjectInfo
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.stytchObjectMapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -22,9 +27,15 @@ internal class MemberImpl(
private val sessionStorage: B2BSessionStorage,
private val api: StytchB2BApi.Member,
) : Member {
private val callbacks = mutableListOf<(MemberData?) -> Unit>()
private val callbacks = mutableListOf<(StytchObjectInfo<MemberData>) -> Unit>()

override val onChange: StateFlow<MemberData?> = sessionStorage.memberFlow
override val onChange: StateFlow<StytchObjectInfo<MemberData>> =
combine(sessionStorage.memberFlow, sessionStorage.lastValidatedAtFlow, ::stytchObjectMapper)
.stateIn(
externalScope,
SharingStarted.WhileSubscribed(),
stytchObjectMapper(sessionStorage.member, sessionStorage.lastValidatedAt),
)

init {
externalScope.launch {
Expand All @@ -36,7 +47,7 @@ internal class MemberImpl(
}
}

override fun onChange(callback: (MemberData?) -> Unit) {
override fun onChange(callback: (StytchObjectInfo<MemberData>) -> Unit) {
callbacks.add(callback)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.stytch.sdk.b2b.network.models.MfaPolicy
import com.stytch.sdk.b2b.network.models.OrganizationData
import com.stytch.sdk.b2b.network.models.SearchOperator
import com.stytch.sdk.b2b.network.models.SsoJitProvisioning
import com.stytch.sdk.common.StytchObjectInfo
import kotlinx.coroutines.flow.StateFlow
import java.util.concurrent.CompletableFuture

Expand All @@ -32,13 +33,13 @@ public interface Organization {
/**
* Exposes a flow of organization data
*/
public val onChange: StateFlow<OrganizationData?>
public val onChange: StateFlow<StytchObjectInfo<OrganizationData>>

/**
* Assign a callback that will be called when the organization data changes
*/

public fun onChange(callback: (OrganizationData?) -> Unit)
public fun onChange(callback: (StytchObjectInfo<OrganizationData>) -> Unit)

/**
* Wraps Stytch’s organization/me endpoint.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,15 @@ import com.stytch.sdk.b2b.network.models.B2BRequests
import com.stytch.sdk.b2b.network.models.OrganizationData
import com.stytch.sdk.b2b.sessions.B2BSessionStorage
import com.stytch.sdk.common.StytchDispatchers
import com.stytch.sdk.common.StytchObjectInfo
import com.stytch.sdk.common.StytchResult
import com.stytch.sdk.common.stytchObjectMapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.future.asCompletableFuture
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -30,9 +35,15 @@ internal class OrganizationImpl(
private val sessionStorage: B2BSessionStorage,
private val api: StytchB2BApi.Organization,
) : Organization {
private val callbacks = mutableListOf<(OrganizationData?) -> Unit>()
private val callbacks = mutableListOf<(StytchObjectInfo<OrganizationData>) -> Unit>()

override val onChange: StateFlow<OrganizationData?> = sessionStorage.organizationFlow
override val onChange: StateFlow<StytchObjectInfo<OrganizationData>> =
combine(sessionStorage.organizationFlow, sessionStorage.lastValidatedAtFlow, ::stytchObjectMapper)
.stateIn(
externalScope,
SharingStarted.WhileSubscribed(),
stytchObjectMapper<OrganizationData>(sessionStorage.organization, sessionStorage.lastValidatedAt),
)

init {
externalScope.launch {
Expand All @@ -44,7 +55,7 @@ internal class OrganizationImpl(
}
}

override fun onChange(callback: (OrganizationData?) -> Unit) {
override fun onChange(callback: (StytchObjectInfo<OrganizationData>) -> Unit) {
callbacks.add(callback)
}

Expand Down
Loading

0 comments on commit 7e1c1e8

Please sign in to comment.