improvement: Show actual JSON when a cipher fails the decoding #224
This commit is contained in:
parent
1d170fdaeb
commit
a1210b4ef8
|
@ -31,6 +31,7 @@ data class BitwardenService(
|
||||||
data class Error(
|
data class Error(
|
||||||
val code: Int,
|
val code: Int,
|
||||||
val message: String? = null,
|
val message: String? = null,
|
||||||
|
val blob: String? = null,
|
||||||
val revisionDate: Instant,
|
val revisionDate: Instant,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package com.artemchep.keyguard.feature.home.vault.component
|
package com.artemchep.keyguard.feature.home.vault.component
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.IntrinsicSize
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
@ -11,6 +15,7 @@ import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.AccessTime
|
import androidx.compose.material.icons.outlined.AccessTime
|
||||||
|
import androidx.compose.material.icons.outlined.ContentCopy
|
||||||
import androidx.compose.material.icons.outlined.ErrorOutline
|
import androidx.compose.material.icons.outlined.ErrorOutline
|
||||||
import androidx.compose.material.icons.outlined.Sync
|
import androidx.compose.material.icons.outlined.Sync
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
@ -24,7 +29,11 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import com.artemchep.keyguard.feature.home.vault.model.VaultViewItem
|
import com.artemchep.keyguard.feature.home.vault.model.VaultViewItem
|
||||||
import com.artemchep.keyguard.ui.DisabledEmphasisAlpha
|
import com.artemchep.keyguard.ui.DisabledEmphasisAlpha
|
||||||
import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
|
import com.artemchep.keyguard.ui.ExpandedIfNotEmpty
|
||||||
|
@ -122,6 +131,83 @@ fun VaultViewErrorItem(
|
||||||
.width(8.dp),
|
.width(8.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (item.blob != null) Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.height(IntrinsicSize.Min)
|
||||||
|
.background(
|
||||||
|
color = LocalContentColor.current
|
||||||
|
.combineAlpha(0.1f),
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp),
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp)
|
||||||
|
.background(
|
||||||
|
color = LocalContentColor.current
|
||||||
|
.combineAlpha(MediumEmphasisAlpha),
|
||||||
|
)
|
||||||
|
.fillMaxHeight(),
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp),
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = modifier
|
||||||
|
.padding(
|
||||||
|
vertical = 8.dp,
|
||||||
|
),
|
||||||
|
text = "BLOB",
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
fontSize = 12.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = LocalContentColor.current
|
||||||
|
.combineAlpha(MediumEmphasisAlpha),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(),
|
||||||
|
text = item.blob,
|
||||||
|
maxLines = 3,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontSize = 13.sp,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp),
|
||||||
|
)
|
||||||
|
val updatedOnCopy by rememberUpdatedState(item.onCopyBlob)
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
updatedOnCopy?.invoke()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.ContentCopy,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(8.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -111,8 +111,10 @@ sealed interface VaultViewItem {
|
||||||
override val id: String,
|
override val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val message: String?,
|
val message: String?,
|
||||||
|
val blob: String? = null,
|
||||||
val timestamp: String,
|
val timestamp: String,
|
||||||
val onRetry: (() -> Unit)? = null,
|
val onRetry: (() -> Unit)? = null,
|
||||||
|
val onCopyBlob: (() -> Unit)? = null,
|
||||||
) : VaultViewItem {
|
) : VaultViewItem {
|
||||||
companion object;
|
companion object;
|
||||||
}
|
}
|
||||||
|
|
|
@ -819,6 +819,7 @@ private fun RememberStateFlowScope.oh(
|
||||||
id = "error",
|
id = "error",
|
||||||
name = "Couldn't sync the item",
|
name = "Couldn't sync the item",
|
||||||
message = cipherError.message(),
|
message = cipherError.message(),
|
||||||
|
blob = cipherError.blob,
|
||||||
timestamp = time,
|
timestamp = time,
|
||||||
onRetry = if (cipherError.canRetry(cipher.revisionDate)) {
|
onRetry = if (cipherError.canRetry(cipher.revisionDate)) {
|
||||||
// lambda
|
// lambda
|
||||||
|
@ -830,6 +831,17 @@ private fun RememberStateFlowScope.oh(
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
onCopyBlob = if (cipherError.blob != null) {
|
||||||
|
// lambda
|
||||||
|
{
|
||||||
|
copy.copy(
|
||||||
|
text = cipherError.blob,
|
||||||
|
hidden = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
)
|
)
|
||||||
emit(model)
|
emit(model)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,8 @@ import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.NoTransformationFoundException
|
import io.ktor.client.call.NoTransformationFoundException
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
import kotlinx.datetime.Instant
|
import kotlinx.datetime.Instant
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import kotlin.RuntimeException
|
import kotlin.RuntimeException
|
||||||
import kotlin.String
|
import kotlin.String
|
||||||
import kotlin.TODO
|
import kotlin.TODO
|
||||||
|
@ -73,6 +75,7 @@ import kotlin.to
|
||||||
class SyncEngine(
|
class SyncEngine(
|
||||||
private val httpClient: HttpClient,
|
private val httpClient: HttpClient,
|
||||||
private val dbManager: DatabaseManager,
|
private val dbManager: DatabaseManager,
|
||||||
|
private val json: Json,
|
||||||
private val base64Service: Base64Service,
|
private val base64Service: Base64Service,
|
||||||
private val cryptoGenerator: CryptoGenerator,
|
private val cryptoGenerator: CryptoGenerator,
|
||||||
private val cipherEncryptor: CipherEncryptor,
|
private val cipherEncryptor: CipherEncryptor,
|
||||||
|
@ -89,6 +92,32 @@ class SyncEngine(
|
||||||
|
|
||||||
class DecodeVaultException(message: String, e: Throwable) : RuntimeException(message, e)
|
class DecodeVaultException(message: String, e: Throwable) : RuntimeException(message, e)
|
||||||
|
|
||||||
|
private inline fun <reified T> createDecodingFailedServiceModel(
|
||||||
|
now: Instant,
|
||||||
|
model: T,
|
||||||
|
lens: SyncManager.Lens<T>,
|
||||||
|
): BitwardenService {
|
||||||
|
val revisionDate = lens.getRevisionDate(model)
|
||||||
|
.takeUnless { it == Instant.DISTANT_FUTURE }
|
||||||
|
?: now
|
||||||
|
|
||||||
|
val blob = json.encodeToString(model)
|
||||||
|
return BitwardenService(
|
||||||
|
remote = BitwardenService.Remote(
|
||||||
|
id = lens.getId(model),
|
||||||
|
revisionDate = revisionDate,
|
||||||
|
deletedDate = lens.getDeletedDate(model),
|
||||||
|
),
|
||||||
|
error = BitwardenService.Error(
|
||||||
|
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
||||||
|
blob = blob,
|
||||||
|
revisionDate = now,
|
||||||
|
),
|
||||||
|
deleted = false,
|
||||||
|
version = BitwardenService.VERSION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun sync() = kotlin.run {
|
suspend fun sync() = kotlin.run {
|
||||||
val env = user.env.back()
|
val env = user.env.back()
|
||||||
val api = env.api
|
val api = env.api
|
||||||
|
@ -264,6 +293,10 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
|
|
||||||
val folderDao = db.folderQueries
|
val folderDao = db.folderQueries
|
||||||
|
val folderRemoteLens = SyncManager.Lens<FolderEntity>(
|
||||||
|
getId = { it.id },
|
||||||
|
getRevisionDate = { it.revisionDate },
|
||||||
|
)
|
||||||
val existingFolders = folderDao
|
val existingFolders = folderDao
|
||||||
.getByAccountId(
|
.getByAccountId(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -345,18 +378,10 @@ class SyncEngine(
|
||||||
|
|
||||||
val localId = localOrNull?.folderId
|
val localId = localOrNull?.folderId
|
||||||
?: cryptoGenerator.uuid()
|
?: cryptoGenerator.uuid()
|
||||||
val service = BitwardenService(
|
val service = createDecodingFailedServiceModel(
|
||||||
remote = BitwardenService.Remote(
|
now = now,
|
||||||
id = remote.id,
|
model = remote,
|
||||||
revisionDate = remote.revisionDate,
|
lens = folderRemoteLens,
|
||||||
deletedDate = null,
|
|
||||||
),
|
|
||||||
error = BitwardenService.Error(
|
|
||||||
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
|
||||||
revisionDate = now,
|
|
||||||
),
|
|
||||||
deleted = false,
|
|
||||||
version = BitwardenService.VERSION,
|
|
||||||
)
|
)
|
||||||
val model = localOrNull?.copy(service = service) ?: BitwardenFolder(
|
val model = localOrNull?.copy(service = service) ?: BitwardenFolder(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -457,6 +482,11 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
|
|
||||||
val cipherDao = db.cipherQueries
|
val cipherDao = db.cipherQueries
|
||||||
|
val cipherRemoteLens = SyncManager.Lens<CipherEntity>(
|
||||||
|
getId = { it.id },
|
||||||
|
getRevisionDate = { cipher -> cipher.revisionDate },
|
||||||
|
getDeletedDate = { cipher -> cipher.deletedDate },
|
||||||
|
)
|
||||||
val existingCipher = cipherDao
|
val existingCipher = cipherDao
|
||||||
.getByAccountId(
|
.getByAccountId(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -530,11 +560,7 @@ class SyncEngine(
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
remoteItems = response.ciphers.orEmpty(),
|
remoteItems = response.ciphers.orEmpty(),
|
||||||
remoteLens = SyncManager.Lens(
|
remoteLens = cipherRemoteLens,
|
||||||
getId = { it.id },
|
|
||||||
getRevisionDate = { cipher -> cipher.revisionDate },
|
|
||||||
getDeletedDate = { cipher -> cipher.deletedDate },
|
|
||||||
),
|
|
||||||
remoteDecoder = { remote, local ->
|
remoteDecoder = { remote, local ->
|
||||||
val codec = getCodec(
|
val codec = getCodec(
|
||||||
mode = BitwardenCrCta.Mode.DECRYPT,
|
mode = BitwardenCrCta.Mode.DECRYPT,
|
||||||
|
@ -571,18 +597,10 @@ class SyncEngine(
|
||||||
val localId = localOrNull?.cipherId
|
val localId = localOrNull?.cipherId
|
||||||
?: cryptoGenerator.uuid()
|
?: cryptoGenerator.uuid()
|
||||||
val folderId = remote.folderId?.let { remoteToLocalFolders[it] }
|
val folderId = remote.folderId?.let { remoteToLocalFolders[it] }
|
||||||
val service = BitwardenService(
|
val service = createDecodingFailedServiceModel(
|
||||||
remote = BitwardenService.Remote(
|
now = now,
|
||||||
id = remote.id,
|
model = remote,
|
||||||
revisionDate = remote.revisionDate,
|
lens = cipherRemoteLens,
|
||||||
deletedDate = remote.deletedDate,
|
|
||||||
),
|
|
||||||
error = BitwardenService.Error(
|
|
||||||
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
|
||||||
revisionDate = now,
|
|
||||||
),
|
|
||||||
deleted = false,
|
|
||||||
version = BitwardenService.VERSION,
|
|
||||||
)
|
)
|
||||||
val model = localOrNull?.copy(service = service) ?: BitwardenCipher(
|
val model = localOrNull?.copy(service = service) ?: BitwardenCipher(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -732,6 +750,10 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
|
|
||||||
val collectionDao = db.collectionQueries
|
val collectionDao = db.collectionQueries
|
||||||
|
val collectionRemoteLens = SyncManager.Lens<CollectionEntity>(
|
||||||
|
getId = { it.id },
|
||||||
|
getRevisionDate = { Instant.DISTANT_FUTURE },
|
||||||
|
)
|
||||||
val existingCollections = collectionDao
|
val existingCollections = collectionDao
|
||||||
.getByAccountId(
|
.getByAccountId(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -772,10 +794,7 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remoteItems = response.collections.orEmpty(),
|
remoteItems = response.collections.orEmpty(),
|
||||||
remoteLens = SyncManager.Lens<CollectionEntity>(
|
remoteLens = collectionRemoteLens,
|
||||||
getId = { it.id },
|
|
||||||
getRevisionDate = { Instant.DISTANT_FUTURE },
|
|
||||||
),
|
|
||||||
remoteDecoder = { remote, local ->
|
remoteDecoder = { remote, local ->
|
||||||
val codec = getCodec(
|
val codec = getCodec(
|
||||||
mode = BitwardenCrCta.Mode.DECRYPT,
|
mode = BitwardenCrCta.Mode.DECRYPT,
|
||||||
|
@ -796,18 +815,10 @@ class SyncEngine(
|
||||||
)
|
)
|
||||||
recordException(logE)
|
recordException(logE)
|
||||||
|
|
||||||
val service = BitwardenService(
|
val service = createDecodingFailedServiceModel(
|
||||||
remote = BitwardenService.Remote(
|
now = now,
|
||||||
id = remote.id,
|
model = remote,
|
||||||
revisionDate = now,
|
lens = collectionRemoteLens,
|
||||||
deletedDate = null,
|
|
||||||
),
|
|
||||||
error = BitwardenService.Error(
|
|
||||||
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
|
||||||
revisionDate = now,
|
|
||||||
),
|
|
||||||
deleted = false,
|
|
||||||
version = BitwardenService.VERSION,
|
|
||||||
)
|
)
|
||||||
val model = localOrNull?.copy(service = service) ?: BitwardenCollection(
|
val model = localOrNull?.copy(service = service) ?: BitwardenCollection(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -848,6 +859,10 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
|
|
||||||
val organizationDao = db.organizationQueries
|
val organizationDao = db.organizationQueries
|
||||||
|
val organizationRemoteLens = SyncManager.Lens<OrganizationEntity>(
|
||||||
|
getId = { it.id },
|
||||||
|
getRevisionDate = { Instant.DISTANT_FUTURE },
|
||||||
|
)
|
||||||
val existingOrganizations = organizationDao
|
val existingOrganizations = organizationDao
|
||||||
.getByAccountId(
|
.getByAccountId(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -888,10 +903,7 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remoteItems = response.profile.organizations.orEmpty(),
|
remoteItems = response.profile.organizations.orEmpty(),
|
||||||
remoteLens = SyncManager.Lens<OrganizationEntity>(
|
remoteLens = organizationRemoteLens,
|
||||||
getId = { it.id },
|
|
||||||
getRevisionDate = { Instant.DISTANT_FUTURE },
|
|
||||||
),
|
|
||||||
remoteDecoder = { remote, local ->
|
remoteDecoder = { remote, local ->
|
||||||
val codec = getCodec(
|
val codec = getCodec(
|
||||||
mode = BitwardenCrCta.Mode.DECRYPT,
|
mode = BitwardenCrCta.Mode.DECRYPT,
|
||||||
|
@ -911,18 +923,10 @@ class SyncEngine(
|
||||||
)
|
)
|
||||||
recordException(logE)
|
recordException(logE)
|
||||||
|
|
||||||
val service = BitwardenService(
|
val service = createDecodingFailedServiceModel(
|
||||||
remote = BitwardenService.Remote(
|
now = now,
|
||||||
id = remote.id,
|
model = remote,
|
||||||
revisionDate = now,
|
lens = organizationRemoteLens,
|
||||||
deletedDate = null,
|
|
||||||
),
|
|
||||||
error = BitwardenService.Error(
|
|
||||||
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
|
||||||
revisionDate = now,
|
|
||||||
),
|
|
||||||
deleted = false,
|
|
||||||
version = BitwardenService.VERSION,
|
|
||||||
)
|
)
|
||||||
val model = localOrNull?.copy(service = service) ?: BitwardenOrganization(
|
val model = localOrNull?.copy(service = service) ?: BitwardenOrganization(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -965,6 +969,10 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
|
|
||||||
val sendDao = db.sendQueries
|
val sendDao = db.sendQueries
|
||||||
|
val sendRemoteLens = SyncManager.Lens<SyncSends>(
|
||||||
|
getId = { it.id },
|
||||||
|
getRevisionDate = { it.revisionDate },
|
||||||
|
)
|
||||||
val existingSends = sendDao
|
val existingSends = sendDao
|
||||||
.getByAccountId(
|
.getByAccountId(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
@ -1025,10 +1033,7 @@ class SyncEngine(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
remoteItems = response.sends.orEmpty(),
|
remoteItems = response.sends.orEmpty(),
|
||||||
remoteLens = SyncManager.Lens<SyncSends>(
|
remoteLens = sendRemoteLens,
|
||||||
getId = { it.id },
|
|
||||||
getRevisionDate = { it.revisionDate },
|
|
||||||
),
|
|
||||||
remoteDecoder = { remote, local ->
|
remoteDecoder = { remote, local ->
|
||||||
val (
|
val (
|
||||||
itemCrypto,
|
itemCrypto,
|
||||||
|
@ -1053,18 +1058,10 @@ class SyncEngine(
|
||||||
},
|
},
|
||||||
remoteDecodedFallback = { remote, localOrNull, e ->
|
remoteDecodedFallback = { remote, localOrNull, e ->
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
val service = BitwardenService(
|
val service = createDecodingFailedServiceModel(
|
||||||
remote = BitwardenService.Remote(
|
now = now,
|
||||||
id = remote.id,
|
model = remote,
|
||||||
revisionDate = remote.revisionDate,
|
lens = sendRemoteLens,
|
||||||
deletedDate = null,
|
|
||||||
),
|
|
||||||
error = BitwardenService.Error(
|
|
||||||
code = BitwardenService.Error.CODE_DECODING_FAILED,
|
|
||||||
revisionDate = now,
|
|
||||||
),
|
|
||||||
deleted = false,
|
|
||||||
version = BitwardenService.VERSION,
|
|
||||||
)
|
)
|
||||||
val model = localOrNull?.copy(service = service) ?: BitwardenSend(
|
val model = localOrNull?.copy(service = service) ?: BitwardenSend(
|
||||||
accountId = user.id,
|
accountId = user.id,
|
||||||
|
|
|
@ -10,7 +10,7 @@ class SyncManager<Local : BitwardenService.Has<Local>, Remote : Any>(
|
||||||
private val local: LensLocal<Local>,
|
private val local: LensLocal<Local>,
|
||||||
private val remote: Lens<Remote>,
|
private val remote: Lens<Remote>,
|
||||||
) {
|
) {
|
||||||
class Lens<T>(
|
data class Lens<T>(
|
||||||
val getId: (T) -> String,
|
val getId: (T) -> String,
|
||||||
val getRevisionDate: (T) -> Instant,
|
val getRevisionDate: (T) -> Instant,
|
||||||
val getDeletedDate: (T) -> Instant? = { null },
|
val getDeletedDate: (T) -> Instant? = { null },
|
||||||
|
|
|
@ -82,6 +82,7 @@ class SyncByTokenImpl(
|
||||||
val syncEngine = SyncEngine(
|
val syncEngine = SyncEngine(
|
||||||
httpClient = httpClient,
|
httpClient = httpClient,
|
||||||
dbManager = db,
|
dbManager = db,
|
||||||
|
json = json,
|
||||||
base64Service = base64Service,
|
base64Service = base64Service,
|
||||||
cryptoGenerator = cryptoGenerator,
|
cryptoGenerator = cryptoGenerator,
|
||||||
cipherEncryptor = cipherEncryptor,
|
cipherEncryptor = cipherEncryptor,
|
||||||
|
|
Loading…
Reference in New Issue