From 22ca967f441bef469e7767764f36bb4d51eb27a6 Mon Sep 17 00:00:00 2001 From: Mohamad Jaara Date: Mon, 13 Jan 2025 18:28:26 +0100 Subject: [PATCH] feat: database logger --- .../feature/debug/ChangeProfilingUseCase.kt | 9 +- .../kalium/logic/feature/debug/DebugScope.kt | 6 +- .../ObserveDatabaseLoggerStateUseCase.kt | 31 +++++ .../kalium/persistence/db/UserDatabase.kt | 1 - .../kalium/persistence/db/DebugExtension.kt | 107 ++++++++++++++++++ .../persistence/db/UserDatabaseBuilder.kt | 27 ++--- 6 files changed, 156 insertions(+), 25 deletions(-) create mode 100644 logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ObserveDatabaseLoggerStateUseCase.kt create mode 100644 persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/DebugExtension.kt diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ChangeProfilingUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ChangeProfilingUseCase.kt index 83588fea472..211a7586add 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ChangeProfilingUseCase.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ChangeProfilingUseCase.kt @@ -18,6 +18,7 @@ package com.wire.kalium.logic.feature.debug import com.wire.kalium.logic.di.UserStorage +import com.wire.kalium.persistence.db.DBProfile class ChangeProfilingUseCase( private val userStorage: UserStorage, @@ -26,7 +27,11 @@ class ChangeProfilingUseCase( * Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted * @param enabled true to enable profiling, false to disable */ - operator fun invoke(enabled: Boolean) { - userStorage.database.changeProfiling(enabled) + suspend operator fun invoke(status: DBProfile) { + userStorage.database.debugExtension.changeProfiling(status) + } + + suspend operator fun invoke(enabled: Boolean) { + userStorage.database.debugExtension.changeProfiling(if (enabled) DBProfile.ON.Device else DBProfile.Off) } } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt index 34b31cdf4b7..a6da4e48459 100644 --- a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/DebugScope.kt @@ -93,7 +93,7 @@ class DebugScope internal constructor( private val legalHoldHandler: LegalHoldHandler, private val notificationTokenRepository: NotificationTokenRepository, private val scope: CoroutineScope, - userStorage: UserStorage, + private val userStorage: UserStorage, logger: KaliumLogger, internal val dispatcher: KaliumDispatcher = KaliumDispatcherImpl, ) { @@ -227,5 +227,7 @@ class DebugScope internal constructor( notificationTokenRepository, ) - val changeProfiling: ChangeProfilingUseCase = ChangeProfilingUseCase(userStorage) + val changeProfiling: ChangeProfilingUseCase get() = ChangeProfilingUseCase(userStorage) + + val observeDatabaseLoggerState get() = ObserveDatabaseLoggerStateUseCase(userStorage) } diff --git a/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ObserveDatabaseLoggerStateUseCase.kt b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ObserveDatabaseLoggerStateUseCase.kt new file mode 100644 index 00000000000..468fe82fe60 --- /dev/null +++ b/logic/src/commonMain/kotlin/com/wire/kalium/logic/feature/debug/ObserveDatabaseLoggerStateUseCase.kt @@ -0,0 +1,31 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.logic.feature.debug + +import com.wire.kalium.logic.di.UserStorage +import com.wire.kalium.persistence.db.DBProfile +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class ObserveDatabaseLoggerStateUseCase( + private val userStorage: UserStorage, +) { + suspend operator fun invoke(): Flow = userStorage.database.debugExtension.getProfilingState().map { + it is DBProfile.ON + } +} diff --git a/persistence/src/androidMain/kotlin/com/wire/kalium/persistence/db/UserDatabase.kt b/persistence/src/androidMain/kotlin/com/wire/kalium/persistence/db/UserDatabase.kt index 4491d1bf47c..84f63d0a743 100644 --- a/persistence/src/androidMain/kotlin/com/wire/kalium/persistence/db/UserDatabase.kt +++ b/persistence/src/androidMain/kotlin/com/wire/kalium/persistence/db/UserDatabase.kt @@ -57,7 +57,6 @@ actual fun userDatabaseBuilder( dispatcher = dispatcher, platformDatabaseData = platformDatabaseData, isEncrypted = isEncryptionEnabled, - cipherProfile = "logcat", ) } diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/DebugExtension.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/DebugExtension.kt new file mode 100644 index 00000000000..f9dfd619555 --- /dev/null +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/DebugExtension.kt @@ -0,0 +1,107 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.kalium.persistence.db + +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import com.wire.kalium.persistence.dao.MetadataDAO +import com.wire.kalium.persistence.kaliumLogger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class DebugExtension( + private val sqlDriver: SqlDriver, + private val isEncrypted: Boolean, + private val metaDataDao: MetadataDAO, +) { + + suspend fun getProfilingState(): Flow = + metaDataDao.valueByKeyFlow(KEY_CIPHER_PROFILE) + .map { + it?.let { DBProfile.fromString(it) } + } + + /** + * Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted + * @param enabled true to enable profiling, false to disable + */ + suspend fun changeProfiling(state: DBProfile): Long? = + if (isEncrypted) { + sqlDriver.executeQuery( + identifier = null, + sql = """PRAGMA cipher_profile= '${state.logTarget}';""", + mapper = { cursor -> + cursor.next() + cursor.getLong(0).let { QueryResult.Value(it) } + }, + parameters = 0, + ).value.also { + updateMetadata(state) + } + + } else { + error("Cannot change profiling on unencrypted database") + } + + private suspend fun updateMetadata(state: DBProfile) { + metaDataDao.insertValue( + value = state.logTarget, + key = KEY_CIPHER_PROFILE + ) + } + + private companion object { + const val KEY_CIPHER_PROFILE = "cipher_profile" + } +} + +sealed interface DBProfile { + val logTarget: String + + data object Off : DBProfile { + override val logTarget: String = "off" + + override fun toString(): String { + return "off" + } + } + + sealed interface ON : DBProfile { + data object Device : ON { + override val logTarget: String = "logcat" + + override fun toString(): String { + return "logcat" + } + } + + data class CustomFile(override val logTarget: String) : ON { + override fun toString(): String { + return logTarget + } + } + } + + companion object { + fun fromString(value: String): DBProfile = when (value) { + "off" -> Off + "logcat" -> ON.Device + else -> ON.CustomFile(value) + } + } +} diff --git a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/UserDatabaseBuilder.kt b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/UserDatabaseBuilder.kt index 76f9ab33e17..7bbebca1f26 100644 --- a/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/UserDatabaseBuilder.kt +++ b/persistence/src/commonMain/kotlin/com/wire/kalium/persistence/db/UserDatabaseBuilder.kt @@ -120,7 +120,6 @@ class UserDatabaseBuilder internal constructor( private val platformDatabaseData: PlatformDatabaseData, private val isEncrypted: Boolean, private val queriesContext: CoroutineContext = KaliumDispatcherImpl.io, - private val cipherProfile: String? = null, ) { internal val database: UserDatabase = UserDatabase( @@ -314,30 +313,18 @@ class UserDatabaseBuilder internal constructor( queriesContext ) + val debugExtension: DebugExtension + get() = DebugExtension( + sqlDriver = sqlDriver, + metaDataDao = metadataDAO, + isEncrypted = isEncrypted + ) + /** * @return the absolute path of the DB file or null if the DB file does not exist */ fun dbFileLocation(): String? = getDatabaseAbsoluteFileLocation(platformDatabaseData, userId) - /** - * Changes the profiling of the database (cipher_profile) if the profile is specified and the database is encrypted - * @param enabled true to enable profiling, false to disable - */ - fun changeProfiling(enabled: Boolean) { - if (isEncrypted && cipherProfile != null) { - val cipherProfileValue = if (enabled) cipherProfile else "off" - sqlDriver.executeQuery( - identifier = null, - sql = "PRAGMA cipher_profile='$cipherProfileValue'", - mapper = { - it.next() - it.getLong(0).let { QueryResult.Value(it) } - }, - parameters = 0, - ) - } - } - /** * drops DB connection and delete the DB file */