Skip to content
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

feat(backup): add more types - metadata assets (WPB-14589) #3128

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backup/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ kotlin {

// Libsodium
implementation(libs.libsodiumBindingsMP)
api(libs.kermit)
}
}
val commonTest by getting {
Expand Down
26 changes: 26 additions & 0 deletions backup/src/commonMain/kotlin/com/wire/backup/data/BackupData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,35 @@ sealed class BackupMessageContent {
val assetToken: String?,
val assetDomain: String?,
val encryption: EncryptionAlgorithm?,
val metaData: AssetMetadata?,
) : BackupMessageContent() {
enum class EncryptionAlgorithm {
AES_GCM, AES_CBC
}

sealed class AssetMetadata {
data class Image(
val width: Int,
val height: Int,
val tag: String?
) : AssetMetadata()

data class Video(
val width: Int?,
val height: Int?,
val duration: Long?,
) : AssetMetadata()

data class Audio(
val normalization: ByteArray?,
val duration: Long?,
) : AssetMetadata()

data class Generic(
val name: String?,
) : AssetMetadata()
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
Expand All @@ -129,6 +153,7 @@ sealed class BackupMessageContent {
if (assetToken != other.assetToken) return false
if (assetDomain != other.assetDomain) return false
if (encryption != other.encryption) return false
if (metaData != other.metaData) return false

return true
}
Expand All @@ -140,6 +165,7 @@ sealed class BackupMessageContent {
result = 31 * result + (assetToken?.hashCode() ?: 0)
result = 31 * result + (assetDomain?.hashCode() ?: 0)
result = 31 * result + (encryption?.hashCode() ?: 0)
result = 31 * result + (metaData?.hashCode() ?: 0)
return result
}
}
Expand Down
139 changes: 103 additions & 36 deletions backup/src/commonMain/kotlin/com/wire/backup/ingest/MPBackupMapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.wire.backup.ingest

import co.touchlab.kermit.Logger
import com.wire.backup.data.BackupConversation
import com.wire.backup.data.BackupData
import com.wire.backup.data.BackupDateTime
Expand All @@ -30,12 +31,16 @@ import com.wire.backup.data.toModel
import com.wire.backup.data.toProtoModel
import com.wire.kalium.protobuf.backup.ExportUser
import com.wire.kalium.protobuf.backup.ExportedAsset
import com.wire.kalium.protobuf.backup.ExportedAudioMetaData
import com.wire.kalium.protobuf.backup.ExportedConversation
import com.wire.kalium.protobuf.backup.ExportedEncryptionAlgorithm
import com.wire.kalium.protobuf.backup.ExportedGenericMetaData
import com.wire.kalium.protobuf.backup.ExportedImageMetaData
import com.wire.kalium.protobuf.backup.ExportedLocation
import com.wire.kalium.protobuf.backup.ExportedMessage
import com.wire.kalium.protobuf.backup.ExportedMessage.Content
import com.wire.kalium.protobuf.backup.ExportedText
import com.wire.kalium.protobuf.backup.ExportedVideoMetaData
import pbandk.ByteArr
import com.wire.kalium.protobuf.backup.BackupData as ProtoBackupData

Expand All @@ -47,46 +52,83 @@ internal class MPBackupMapper {
handle = it.handle
)

fun mapMessageToProtobuf(it: BackupMessage) = ExportedMessage(
id = it.id,
timeIso = it.creationDate.toLongMilliseconds(),
senderUserId = it.senderUserId.toProtoModel(),
senderClientId = it.senderClientId,
conversationId = it.conversationId.toProtoModel(),
content = when (val content = it.content) {
is BackupMessageContent.Asset ->
Content.Asset(
ExportedAsset(
content.mimeType,
content.size.toLong(),
content.name,
ByteArr(content.otrKey),
ByteArr(content.sha256),
content.assetId,
content.assetToken,
content.assetDomain,
when (content.encryption) {
EncryptionAlgorithm.AES_GCM -> ExportedEncryptionAlgorithm.BACKUP_AES_GCM
EncryptionAlgorithm.AES_CBC -> ExportedEncryptionAlgorithm.BACKUP_AES_CBC
null -> null
}
@Suppress("LongMethod")
fun mapMessageToProtobuf(it: BackupMessage): ExportedMessage {
return ExportedMessage(
id = it.id,
timeIso = it.creationDate.toLongMilliseconds(),
senderUserId = it.senderUserId.toProtoModel(),
senderClientId = it.senderClientId,
conversationId = it.conversationId.toProtoModel(),
content = when (val content = it.content) {
is BackupMessageContent.Asset -> {
Logger.d("MPBackupMapper") { "Mapping asset message to protobuf: ${content.metaData}" }
Content.Asset(
ExportedAsset(
content.mimeType,
content.size.toLong(),
content.name,
ByteArr(content.otrKey),
ByteArr(content.sha256),
content.assetId,
content.assetToken,
content.assetDomain,
when (content.encryption) {
EncryptionAlgorithm.AES_GCM -> ExportedEncryptionAlgorithm.BACKUP_AES_GCM
EncryptionAlgorithm.AES_CBC -> ExportedEncryptionAlgorithm.BACKUP_AES_CBC
null -> null
},
content.metaData?.let {
when (it) {
is BackupMessageContent.Asset.AssetMetadata.Audio ->
ExportedAsset.MetaData.Audio(
ExportedAudioMetaData(
it.duration,
it.normalization?.let { ByteArr(it) }
)
)

is BackupMessageContent.Asset.AssetMetadata.Image ->
ExportedAsset.MetaData.Image(
ExportedImageMetaData(
it.width,
it.height,
it.tag
)
)

is BackupMessageContent.Asset.AssetMetadata.Video ->
ExportedAsset.MetaData.Video(
ExportedVideoMetaData(
it.width,
it.height,
it.duration
)
)

is BackupMessageContent.Asset.AssetMetadata.Generic ->
ExportedAsset.MetaData.Generic(ExportedGenericMetaData(it.name))
}
}
)
)
)
}

is BackupMessageContent.Text ->
Content.Text(ExportedText(content.text))
is BackupMessageContent.Text ->
Content.Text(ExportedText(content.text))

is BackupMessageContent.Location -> Content.Location(
ExportedLocation(
content.longitude,
content.latitude,
content.name,
content.zoom
is BackupMessageContent.Location -> Content.Location(
ExportedLocation(
content.longitude,
content.latitude,
content.name,
content.zoom
)
)
)
},
webPk = it.webPrimaryKey?.toLong()
)
},
webPk = it.webPrimaryKey?.toLong()
)
}

fun mapConversationToProtobuf(it: BackupConversation) = ExportedConversation(it.id.toProtoModel(), it.name)

Expand All @@ -112,6 +154,7 @@ internal class MPBackupMapper {
)
}

@Suppress("LongMethod")
private fun fromMessageProtoToBackupModel(message: ExportedMessage): BackupMessage {
val content = when (val protoContent = message.content) {
is Content.Text -> {
Expand All @@ -133,6 +176,30 @@ internal class MPBackupMapper {
ExportedEncryptionAlgorithm.BACKUP_AES_GCM -> EncryptionAlgorithm.AES_GCM
is ExportedEncryptionAlgorithm.UNRECOGNIZED -> null
}
},
protoContent.value.metaData?.let {
when (it) {
is ExportedAsset.MetaData.Audio -> BackupMessageContent.Asset.AssetMetadata.Audio(
it.value.normalizedLoudness?.array,
it.value.durationInMillis
)

is ExportedAsset.MetaData.Image -> BackupMessageContent.Asset.AssetMetadata.Image(
it.value.width,
it.value.height,
it.value.tag
)

is ExportedAsset.MetaData.Video -> BackupMessageContent.Asset.AssetMetadata.Video(
it.value.width,
it.value.height,
it.value.durationInMillis
)

is ExportedAsset.MetaData.Generic -> BackupMessageContent.Asset.AssetMetadata.Generic(
it.value.name
)
}
}
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,19 @@ class BackupEndToEndTest {
fun givenBackedUpAssetMessage_whenRestoring_thenShouldReadTheSameContent() = runTest {
val content = BackupMessageContent.Asset(
mimeType = "image/jpeg",
size = 42,
size = 64,
name = "pudim.jpg",
otrKey = byteArrayOf(31),
sha256 = byteArrayOf(33),
assetId = "assetId",
assetToken = "token",
assetDomain = "domain",
encryption = BackupMessageContent.Asset.EncryptionAlgorithm.AES_GCM
encryption = BackupMessageContent.Asset.EncryptionAlgorithm.AES_GCM,
metaData = BackupMessageContent.Asset.AssetMetadata.Video(
duration = 42,
width = 800,
height = 600,
)
)
shouldBackupAndRestoreSameContent(content)
}
Expand Down
27 changes: 27 additions & 0 deletions protobuf-codegen/src/main/proto/backup.proto
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,33 @@ message ExportedAsset {
optional string asset_token = 7;
optional string asset_domain = 8;
optional ExportedEncryptionAlgorithm encryption = 9;
oneof meta_data {
ExportedImageMetaData image = 10;
ExportedVideoMetaData video = 11;
ExportedAudioMetaData audio = 12;
ExportedGenericMetaData generic = 13;
}
}

message ExportedImageMetaData {
required int32 width = 1;
required int32 height = 2;
optional string tag = 3;
}

message ExportedVideoMetaData {
optional int32 width = 1;
optional int32 height = 2;
optional uint64 duration_in_millis = 3;
}

message ExportedAudioMetaData {
optional uint64 duration_in_millis = 1;
optional bytes normalized_loudness = 2; // each byte represent one loudness value as a byte (char) value.
}

message ExportedGenericMetaData {
optional string name = 1;
}

message ExportedLocation {
Expand Down
Loading