Skip to content

Commit

Permalink
feat(backup): add more types - metadata assets (WPB-14589) (#3128)
Browse files Browse the repository at this point in the history
* feat: add metadata for assetd

* feat: add metadata for assetd

* fix: detekt

* feat: metadata logs

* feat: generic metadata for images
  • Loading branch information
yamilmedina authored and vitorhugods committed Dec 2, 2024
1 parent 79e988b commit 6157bf2
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 38 deletions.
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

0 comments on commit 6157bf2

Please sign in to comment.