mirror of
https://github.com/bitwarden/android.git
synced 2026-06-08 16:17:05 -05:00
BIT-1362 Receive and expose push notification events from PushManager (#581)
This commit is contained in:
committed by
Álison Fernandes
parent
739004cc57
commit
5a2b1e61c2
@@ -1,9 +1,73 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncSendDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncSendUpsertData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Manager to handle push notification registration.
|
||||
*/
|
||||
interface PushManager {
|
||||
/**
|
||||
* Flow that represents requests intended for full syncs.
|
||||
*/
|
||||
val fullSyncFlow: Flow<Unit>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to log a user out.
|
||||
*/
|
||||
val logoutFlow: Flow<Unit>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a passwordless request.
|
||||
*/
|
||||
val passwordlessRequestFlow: Flow<PasswordlessRequestData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync cipher delete.
|
||||
*/
|
||||
val syncCipherDeleteFlow: Flow<SyncCipherDeleteData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync cipher upsert.
|
||||
*/
|
||||
val syncCipherUpsertFlow: Flow<SyncCipherUpsertData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync cipher delete.
|
||||
*/
|
||||
val syncFolderDeleteFlow: Flow<SyncFolderDeleteData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync folder upsert.
|
||||
*/
|
||||
val syncFolderUpsertFlow: Flow<SyncFolderUpsertData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger syncing organization keys.
|
||||
*/
|
||||
val syncOrgKeysFlow: Flow<Unit>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync send delete.
|
||||
*/
|
||||
val syncSendDeleteFlow: Flow<SyncSendDeleteData>
|
||||
|
||||
/**
|
||||
* Flow that represents requests intended to trigger a sync send upsert.
|
||||
*/
|
||||
val syncSendUpsertFlow: Flow<SyncSendUpsertData>
|
||||
|
||||
/**
|
||||
* Handles the necessary steps to take when a push notification with payload [data] is received.
|
||||
*/
|
||||
fun onMessageReceived(data: String)
|
||||
|
||||
/**
|
||||
* Registers a [token] for the current user with Bitwarden's server if needed.
|
||||
*/
|
||||
|
||||
@@ -5,12 +5,27 @@ import com.x8bit.bitwarden.data.platform.datasource.disk.PushDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.model.PushTokenRequest
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.service.PushService
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.BitwardenNotification
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.NotificationPayload
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.NotificationType
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.PasswordlessRequestData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncCipherUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncFolderUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncSendDeleteData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SyncSendUpsertData
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromJsonElement
|
||||
import java.time.Clock
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
@@ -25,11 +40,60 @@ class PushManagerImpl @Inject constructor(
|
||||
private val pushDiskSource: PushDiskSource,
|
||||
private val pushService: PushService,
|
||||
private val clock: Clock,
|
||||
private val json: Json,
|
||||
dispatcherManager: DispatcherManager,
|
||||
) : PushManager {
|
||||
private val ioScope = CoroutineScope(dispatcherManager.io)
|
||||
private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)
|
||||
|
||||
private val mutableFullSyncSharedFlow = bufferedMutableSharedFlow<Unit>()
|
||||
private val mutableLogoutSharedFlow = bufferedMutableSharedFlow<Unit>()
|
||||
private val mutablePasswordlessRequestSharedFlow =
|
||||
bufferedMutableSharedFlow<PasswordlessRequestData>()
|
||||
private val mutableSyncCipherDeleteSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncCipherDeleteData>()
|
||||
private val mutableSyncCipherUpsertSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncCipherUpsertData>()
|
||||
private val mutableSyncFolderDeleteSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncFolderDeleteData>()
|
||||
private val mutableSyncFolderUpsertSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncFolderUpsertData>()
|
||||
private val mutableSyncOrgKeysSharedFlow = bufferedMutableSharedFlow<Unit>()
|
||||
private val mutableSyncSendDeleteSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncSendDeleteData>()
|
||||
private val mutableSyncSendUpsertSharedFlow =
|
||||
bufferedMutableSharedFlow<SyncSendUpsertData>()
|
||||
|
||||
override val fullSyncFlow: SharedFlow<Unit>
|
||||
get() = mutableFullSyncSharedFlow.asSharedFlow()
|
||||
|
||||
override val logoutFlow: SharedFlow<Unit>
|
||||
get() = mutableLogoutSharedFlow.asSharedFlow()
|
||||
|
||||
override val passwordlessRequestFlow: SharedFlow<PasswordlessRequestData>
|
||||
get() = mutablePasswordlessRequestSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncCipherDeleteFlow: SharedFlow<SyncCipherDeleteData>
|
||||
get() = mutableSyncCipherDeleteSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncCipherUpsertFlow: SharedFlow<SyncCipherUpsertData>
|
||||
get() = mutableSyncCipherUpsertSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncFolderDeleteFlow: SharedFlow<SyncFolderDeleteData>
|
||||
get() = mutableSyncFolderDeleteSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncFolderUpsertFlow: SharedFlow<SyncFolderUpsertData>
|
||||
get() = mutableSyncFolderUpsertSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncOrgKeysFlow: SharedFlow<Unit>
|
||||
get() = mutableSyncOrgKeysSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncSendDeleteFlow: SharedFlow<SyncSendDeleteData>
|
||||
get() = mutableSyncSendDeleteSharedFlow.asSharedFlow()
|
||||
|
||||
override val syncSendUpsertFlow: SharedFlow<SyncSendUpsertData>
|
||||
get() = mutableSyncSendUpsertSharedFlow.asSharedFlow()
|
||||
|
||||
init {
|
||||
authDiskSource
|
||||
.userStateFlow
|
||||
@@ -39,6 +103,127 @@ class PushManagerImpl @Inject constructor(
|
||||
.launchIn(unconfinedScope)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount")
|
||||
override fun onMessageReceived(data: String) {
|
||||
val notification = try {
|
||||
json.decodeFromString<BitwardenNotification>(data)
|
||||
} catch (exception: IllegalArgumentException) {
|
||||
return
|
||||
}
|
||||
|
||||
if (authDiskSource.uniqueAppId == notification.contextId) return
|
||||
|
||||
val userId = authDiskSource.userState?.activeUserId
|
||||
|
||||
when (val type = notification.notificationType) {
|
||||
NotificationType.AUTH_REQUEST,
|
||||
NotificationType.AUTH_REQUEST_RESPONSE,
|
||||
-> {
|
||||
val payload: NotificationPayload.PasswordlessRequestNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
mutablePasswordlessRequestSharedFlow.tryEmit(
|
||||
PasswordlessRequestData(
|
||||
loginRequestId = payload.id,
|
||||
userId = payload.userId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.LOG_OUT -> {
|
||||
if (userId == null) return
|
||||
mutableLogoutSharedFlow.tryEmit(Unit)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_CIPHER_CREATE,
|
||||
NotificationType.SYNC_CIPHER_UPDATE,
|
||||
-> {
|
||||
val payload: NotificationPayload.SyncCipherNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
mutableSyncCipherUpsertSharedFlow.tryEmit(
|
||||
SyncCipherUpsertData(
|
||||
cipherId = payload.id,
|
||||
revisionDate = payload.revisionDate,
|
||||
isUpdate = type == NotificationType.SYNC_CIPHER_UPDATE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_CIPHER_DELETE,
|
||||
NotificationType.SYNC_LOGIN_DELETE,
|
||||
-> {
|
||||
val payload: NotificationPayload.SyncCipherNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
mutableSyncCipherDeleteSharedFlow.tryEmit(
|
||||
SyncCipherDeleteData(payload.id),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_CIPHERS,
|
||||
NotificationType.SYNC_SETTINGS,
|
||||
NotificationType.SYNC_VAULT,
|
||||
-> {
|
||||
if (userId == null) return
|
||||
mutableFullSyncSharedFlow.tryEmit(Unit)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_FOLDER_CREATE,
|
||||
NotificationType.SYNC_FOLDER_UPDATE,
|
||||
-> {
|
||||
val payload: NotificationPayload.SyncFolderNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
mutableSyncFolderUpsertSharedFlow.tryEmit(
|
||||
SyncFolderUpsertData(
|
||||
folderId = payload.id,
|
||||
revisionDate = payload.revisionDate,
|
||||
isUpdate = type == NotificationType.SYNC_FOLDER_UPDATE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_FOLDER_DELETE -> {
|
||||
val payload: NotificationPayload.SyncFolderNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
|
||||
mutableSyncFolderDeleteSharedFlow.tryEmit(
|
||||
SyncFolderDeleteData(payload.id),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_ORG_KEYS -> {
|
||||
if (userId == null) return
|
||||
mutableSyncOrgKeysSharedFlow.tryEmit(Unit)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_SEND_CREATE,
|
||||
NotificationType.SYNC_SEND_UPDATE,
|
||||
-> {
|
||||
val payload: NotificationPayload.SyncSendNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
mutableSyncSendUpsertSharedFlow.tryEmit(
|
||||
SyncSendUpsertData(
|
||||
sendId = payload.id,
|
||||
revisionDate = payload.revisionDate,
|
||||
isUpdate = type == NotificationType.SYNC_SEND_UPDATE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
NotificationType.SYNC_SEND_DELETE -> {
|
||||
val payload: NotificationPayload.SyncSendNotification =
|
||||
json.decodeFromJsonElement(notification.payload)
|
||||
if (!payload.userMatchesNotification(userId)) return
|
||||
mutableSyncSendDeleteSharedFlow.tryEmit(
|
||||
SyncSendDeleteData(payload.id),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerPushTokenIfNecessary(token: String) {
|
||||
pushDiskSource.registeredPushToken = token
|
||||
val userId = authDiskSource.userState?.activeUserId ?: return
|
||||
@@ -98,7 +283,11 @@ class PushManagerImpl @Inject constructor(
|
||||
onFailure = {
|
||||
// Silently fail. This call will be attempted again the next time the token
|
||||
// registration is done.
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun NotificationPayload.userMatchesNotification(userId: String?): Boolean {
|
||||
return this.userId != null && this.userId == userId
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.Clock
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -28,11 +29,13 @@ object PushManagerModule {
|
||||
pushService: PushService,
|
||||
dispatcherManager: DispatcherManager,
|
||||
clock: Clock,
|
||||
json: Json,
|
||||
): PushManager = PushManagerImpl(
|
||||
authDiskSource = authDiskSource,
|
||||
pushDiskSource = pushDiskSource,
|
||||
pushService = pushService,
|
||||
dispatcherManager = dispatcherManager,
|
||||
clock = clock,
|
||||
json = json,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
/**
|
||||
* Represents a Bitwarden push notification.
|
||||
*
|
||||
* @property contextId The context ID. This is mainly used to check if the push notification
|
||||
* originated from this app.
|
||||
* @property notificationType The type of notication.
|
||||
* @property payload Data associated with the push notification.
|
||||
*/
|
||||
@Serializable
|
||||
data class BitwardenNotification(
|
||||
@SerialName("contextId") val contextId: String,
|
||||
@SerialName("type") val notificationType: NotificationType,
|
||||
@SerialName("payload") val payload: JsonElement,
|
||||
)
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* The payload of a push notification.
|
||||
*/
|
||||
@Serializable
|
||||
sealed class NotificationPayload {
|
||||
/**
|
||||
* The user ID associated with the push notification.
|
||||
*/
|
||||
abstract val userId: String?
|
||||
|
||||
/**
|
||||
* A notification payload for sync cipher operations.
|
||||
*/
|
||||
@Serializable
|
||||
data class SyncCipherNotification(
|
||||
@SerialName("id") val id: String,
|
||||
@SerialName("userId") override val userId: String?,
|
||||
@SerialName("organizationId") val organizationId: String?,
|
||||
@SerialName("collectionIds") val collectionIds: List<String>,
|
||||
@Contextual
|
||||
@SerialName("revisionDate") val revisionDate: ZonedDateTime,
|
||||
) : NotificationPayload()
|
||||
|
||||
/**
|
||||
* A notification payload for sync folder operations.
|
||||
*/
|
||||
@Serializable
|
||||
data class SyncFolderNotification(
|
||||
@SerialName("id") val id: String,
|
||||
@SerialName("userId") override val userId: String,
|
||||
@Contextual
|
||||
@SerialName("revisionDate") val revisionDate: ZonedDateTime,
|
||||
) : NotificationPayload()
|
||||
|
||||
/**
|
||||
* A notification payload for user-based operations.
|
||||
*/
|
||||
@Serializable
|
||||
data class UserNotification(
|
||||
@SerialName("userId") override val userId: String,
|
||||
@Contextual
|
||||
@SerialName("date") val date: ZonedDateTime,
|
||||
) : NotificationPayload()
|
||||
|
||||
/**
|
||||
* A notification payload for sync send operations.
|
||||
*/
|
||||
@Serializable
|
||||
data class SyncSendNotification(
|
||||
@SerialName("id") val id: String,
|
||||
@SerialName("userId") override val userId: String,
|
||||
@Contextual
|
||||
@SerialName("revisionDate") val revisionDate: ZonedDateTime,
|
||||
) : NotificationPayload()
|
||||
|
||||
/**
|
||||
* A notification payload for passwordless requests.
|
||||
*/
|
||||
@Serializable
|
||||
data class PasswordlessRequestNotification(
|
||||
@SerialName("userId") override val userId: String,
|
||||
@SerialName("id") val id: String,
|
||||
) : NotificationPayload()
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseEnumeratedIntSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Possible notification types.
|
||||
*/
|
||||
@Serializable(NotificationTypeSerializer::class)
|
||||
enum class NotificationType {
|
||||
@SerialName("0")
|
||||
SYNC_CIPHER_UPDATE,
|
||||
|
||||
@SerialName("1")
|
||||
SYNC_CIPHER_CREATE,
|
||||
|
||||
@SerialName("2")
|
||||
SYNC_LOGIN_DELETE,
|
||||
|
||||
@SerialName("3")
|
||||
SYNC_FOLDER_DELETE,
|
||||
|
||||
@SerialName("4")
|
||||
SYNC_CIPHERS,
|
||||
|
||||
@SerialName("5")
|
||||
SYNC_VAULT,
|
||||
|
||||
@SerialName("6")
|
||||
SYNC_ORG_KEYS,
|
||||
|
||||
@SerialName("7")
|
||||
SYNC_FOLDER_CREATE,
|
||||
|
||||
@SerialName("8")
|
||||
SYNC_FOLDER_UPDATE,
|
||||
|
||||
@SerialName("9")
|
||||
SYNC_CIPHER_DELETE,
|
||||
|
||||
@SerialName("10")
|
||||
SYNC_SETTINGS,
|
||||
|
||||
@SerialName("11")
|
||||
LOG_OUT,
|
||||
|
||||
@SerialName("12")
|
||||
SYNC_SEND_CREATE,
|
||||
|
||||
@SerialName("13")
|
||||
SYNC_SEND_UPDATE,
|
||||
|
||||
@SerialName("14")
|
||||
SYNC_SEND_DELETE,
|
||||
|
||||
@SerialName("15")
|
||||
AUTH_REQUEST,
|
||||
|
||||
@SerialName("16")
|
||||
AUTH_REQUEST_RESPONSE,
|
||||
}
|
||||
|
||||
@Keep
|
||||
private class NotificationTypeSerializer :
|
||||
BaseEnumeratedIntSerializer<NotificationType>(NotificationType.entries.toTypedArray())
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Required data for passwordless requests.
|
||||
*
|
||||
* @property loginRequestId The login request ID.
|
||||
* @property userId The user ID.
|
||||
*/
|
||||
data class PasswordlessRequestData(
|
||||
val loginRequestId: String,
|
||||
val userId: String,
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Required data for sync cipher delete operations.
|
||||
*
|
||||
* @property cipherId The cipher ID.
|
||||
*/
|
||||
data class SyncCipherDeleteData(
|
||||
val cipherId: String,
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Required data for sync cipher upsert operations.
|
||||
*
|
||||
* @property cipherId The cipher ID.
|
||||
* @property revisionDate The cipher's revision date. This is used to determine if the local copy of
|
||||
* the cipher is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing cipher.
|
||||
*/
|
||||
data class SyncCipherUpsertData(
|
||||
val cipherId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val isUpdate: Boolean,
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Required data for sync folder delete operations.
|
||||
*
|
||||
* @property folderId The folder ID.
|
||||
*/
|
||||
data class SyncFolderDeleteData(
|
||||
val folderId: String,
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Required data for sync folder upsert operations.
|
||||
*
|
||||
* @property folderId The folder ID.
|
||||
* @property revisionDate The folder's revision date. This is used to determine if the local copy of
|
||||
* the folder is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing folder.
|
||||
*/
|
||||
data class SyncFolderUpsertData(
|
||||
val folderId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val isUpdate: Boolean,
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
/**
|
||||
* Required data for sync send delete operations.
|
||||
*
|
||||
* @property sendId The send ID.
|
||||
*/
|
||||
data class SyncSendDeleteData(
|
||||
val sendId: String,
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.model
|
||||
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Required data for sync send upsert operations.
|
||||
*
|
||||
* @property sendId The send ID.
|
||||
* @property revisionDate The send's revision date. This is used to determine if the local copy of
|
||||
* the send is out-of-date.
|
||||
* @property isUpdate Whether or not this is an update of an existing send.
|
||||
*/
|
||||
data class SyncSendUpsertData(
|
||||
val sendId: String,
|
||||
val revisionDate: ZonedDateTime,
|
||||
val isUpdate: Boolean,
|
||||
)
|
||||
Reference in New Issue
Block a user