diff --git a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt index 04f70403..a300972b 100644 --- a/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt +++ b/FloconAndroid/flocon-base/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt @@ -19,6 +19,15 @@ fun floconRegisterDatabase(displayName: String, absolutePath: String) { ) } +fun floconLogDatabaseQuery(dbName: String, sqlQuery: String, bindArgs: List) { + FloconApp.instance?.client?.databasePlugin?.logQuery( + dbName = dbName, + sqlQuery = sqlQuery, + bindArgs = bindArgs, + ) +} + interface FloconDatabasePlugin { fun register(floconDatabaseModel: FloconDatabaseModel) + fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Protocol.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Protocol.kt index 26e9ba9e..bb2e9748 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Protocol.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/Protocol.kt @@ -34,6 +34,7 @@ object Protocol { object Method { const val Query = "query" const val GetDatabases = "getDatabases" + const val LogQuery = "logQuery" } } diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt index a73b1f41..10c97c58 100644 --- a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/FloconDatabasePlugin.kt @@ -13,6 +13,8 @@ import io.github.openflocon.flocon.plugins.database.model.fromdevice.QueryResult import io.github.openflocon.flocon.plugins.database.model.fromdevice.listDeviceDataBaseDataModelToJson import io.github.openflocon.flocon.plugins.database.model.fromdevice.toJson import io.github.openflocon.flocon.plugins.database.model.todevice.DatabaseQueryMessage +import io.github.openflocon.flocon.plugins.database.model.fromdevice.DatabaseQueryLogModel +import io.github.openflocon.flocon.utils.currentTimeMillis import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update @@ -94,4 +96,21 @@ internal class FloconDatabasePluginImpl( registeredDatabases.update { it + floconDatabaseModel } sendAllDatabases(sender) } + + override fun logQuery(dbName: String, sqlQuery: String, bindArgs: List) { + try { + sender.send( + plugin = Protocol.FromDevice.Database.Plugin, + method = Protocol.FromDevice.Database.Method.LogQuery, + body = DatabaseQueryLogModel( + dbName = dbName, + sqlQuery = sqlQuery, + bindArgs = bindArgs.map { it.toString() }, + timestamp = currentTimeMillis(), + ).toJson(), + ) + } catch (t: Throwable) { + FloconLogger.logError("Database logging error", t) + } + } } \ No newline at end of file diff --git a/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt new file mode 100644 index 00000000..e8644e32 --- /dev/null +++ b/FloconAndroid/flocon/src/commonMain/kotlin/io/github/openflocon/flocon/plugins/database/model/fromdevice/DatabaseQueryLogModel.kt @@ -0,0 +1,23 @@ +package io.github.openflocon.flocon.plugins.database.model.fromdevice + +import io.github.openflocon.flocon.core.FloconEncoder +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString + +@Serializable +internal data class DatabaseQueryLogModel( + val dbName: String, + val sqlQuery: String, + val bindArgs: List?, + val timestamp: Long, +) { + fun toJson(): String { + return FloconEncoder.json.encodeToString(this) + } + + companion object { + fun fromJson(json: String): DatabaseQueryLogModel { + return FloconEncoder.json.decodeFromString(json) + } + } +} diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt index b92d83be..8f0737c2 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/MainActivity.kt @@ -16,7 +16,9 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import io.github.openflocon.flocon.Flocon import io.github.openflocon.flocon.FloconLogger @@ -24,6 +26,7 @@ import io.github.openflocon.flocon.myapplication.dashboard.initializeDashboard import io.github.openflocon.flocon.myapplication.database.DogDatabase import io.github.openflocon.flocon.myapplication.database.initializeDatabases import io.github.openflocon.flocon.myapplication.database.initializeInMemoryDatabases +import io.github.openflocon.flocon.myapplication.database.model.DogEntity import io.github.openflocon.flocon.myapplication.deeplinks.initializeDeeplinks import io.github.openflocon.flocon.myapplication.graphql.GraphQlTester import io.github.openflocon.flocon.myapplication.grpc.GrpcController @@ -95,6 +98,9 @@ class MainActivity : ComponentActivity() { setContent { MyApplicationTheme { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + val scope = rememberCoroutineScope() + val context = LocalContext.current + Column(Modifier.fillMaxSize().padding(innerPadding)) { FlowRow( modifier = Modifier @@ -188,6 +194,23 @@ class MainActivity : ComponentActivity() { ) { Text("send analytics event") } + Button( + onClick = { + scope.launch { + DogDatabase.getDatabase(context).dogDao().insertDog( + DogEntity( + id = System.currentTimeMillis(), + name = "Flocon", + breed = "Golden Retriever ${System.currentTimeMillis()}", + age = 6, + pictureUrl = "https://picsum.photos/501/500.jpg", + ) + ) + } + } + ) { + Text("Insert dog in DB") + } } ImagesListView(modifier = Modifier.fillMaxSize()) diff --git a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt index 190e8824..cce1719d 100644 --- a/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt +++ b/FloconAndroid/sample-android-only/src/main/java/io/github/openflocon/flocon/myapplication/database/DogDatabase.kt @@ -8,6 +8,8 @@ import io.github.openflocon.flocon.myapplication.database.dao.DogDao import io.github.openflocon.flocon.myapplication.database.model.DogEntity import io.github.openflocon.flocon.myapplication.database.model.HumanEntity import io.github.openflocon.flocon.myapplication.database.model.HumanWithDogEntity +import io.github.openflocon.flocon.plugins.database.floconLogDatabaseQuery +import java.util.concurrent.Executors @Database( entities = [ @@ -26,12 +28,17 @@ abstract class DogDatabase : RoomDatabase() { private var INSTANCE: DogDatabase? = null fun getDatabase(context: Context): DogDatabase { + val dbName = "dogs_database" return INSTANCE ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, DogDatabase::class.java, - "dogs_database" // Nom du fichier de la base de données Dogs - ).fallbackToDestructiveMigration().build() + dbName + ).setQueryCallback({ sqlQuery, bindArgs -> floconLogDatabaseQuery( + dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs + ) }, Executors.newSingleThreadExecutor()) + .fallbackToDestructiveMigration() + .build() INSTANCE = instance instance } diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt index ae03b68b..6a619992 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/Databases.kt @@ -2,20 +2,29 @@ package io.github.openflocon.flocon.myapplication.multi import android.content.Context import androidx.room.Room +import androidx.room.RoomDatabase import io.github.openflocon.flocon.myapplication.multi.database.DogDatabase import io.github.openflocon.flocon.myapplication.multi.database.FoodDatabase +import io.github.openflocon.flocon.plugins.database.floconLogDatabaseQuery +import java.util.concurrent.Executor +import java.util.concurrent.Executors object Databases { @Volatile private var dogDatabase: DogDatabase? = null fun getDogDatabase(context: Context): DogDatabase { + val dbName = "dogs_database" return dogDatabase ?: synchronized(this) { val instance = Room.databaseBuilder( context.applicationContext, DogDatabase::class.java, "dogs_database" - ).fallbackToDestructiveMigration().build() + ) + .setQueryCallback({ sqlQuery, bindArgs -> floconLogDatabaseQuery( + dbName = dbName, sqlQuery = sqlQuery, bindArgs = bindArgs + ) }, Executors.newSingleThreadExecutor()) + .fallbackToDestructiveMigration().build() dogDatabase = instance instance } diff --git a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt index 9d5da669..5878a203 100644 --- a/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt +++ b/FloconAndroid/sample-multiplatform/src/androidMain/kotlin/io/github/openflocon/flocon/myapplication/multi/MainActivity.kt @@ -12,10 +12,13 @@ import io.github.openflocon.flocon.myapplication.multi.Databases.getDogDatabase import io.github.openflocon.flocon.myapplication.multi.Databases.getFoodDatabase import io.github.openflocon.flocon.myapplication.multi.database.FoodDatabase import io.github.openflocon.flocon.myapplication.multi.database.initializeDatabases +import io.github.openflocon.flocon.myapplication.multi.database.model.DogEntity import io.github.openflocon.flocon.myapplication.multi.sharedpreferences.initializeSharedPreferences import io.github.openflocon.flocon.myapplication.multi.ui.App import io.ktor.client.HttpClient import io.ktor.client.engine.okhttp.OkHttp +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -43,8 +46,11 @@ class MainActivity : ComponentActivity() { DummyHttpKtorCaller.initialize(ktorClient) initializeSharedPreferences(applicationContext) + + val dogDatabase = getDogDatabase(this) + initializeDatabases( - dogDatabase = getDogDatabase(this), + dogDatabase = dogDatabase, foodDatabase = getFoodDatabase(this), ) diff --git a/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt b/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt index f419c51e..140e68bf 100644 --- a/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt +++ b/FloconAndroid/sample-multiplatform/src/commonMain/kotlin/io/github/openflocon/flocon/myapplication/multi/ui/App.kt @@ -16,11 +16,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import io.github.openflocon.flocon.myapplication.multi.DummyHttpKtorCaller import io.github.openflocon.flocon.myapplication.multi.dashboard.initializeDashboard +import io.github.openflocon.flocon.myapplication.multi.database.model.DogEntity import io.github.openflocon.flocon.plugins.analytics.floconAnalytics import io.github.openflocon.flocon.plugins.analytics.model.AnalyticsEvent import io.github.openflocon.flocon.plugins.analytics.model.analyticsProperty import io.github.openflocon.flocon.plugins.tables.floconTable import io.github.openflocon.flocon.plugins.tables.model.toParam +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import kotlin.random.Random @Composable diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/75.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/75.json new file mode 100644 index 00000000..2794764a --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/75.json @@ -0,0 +1,1767 @@ +{ + "formatVersion": 1, + "database": { + "version": 75, + "identityHash": "ec9a85e977d04757b8e2674970e8dc91", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec9a85e977d04757b8e2674970e8dc91')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/76.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/76.json new file mode 100644 index 00000000..b0065648 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/76.json @@ -0,0 +1,1767 @@ +{ + "formatVersion": 1, + "database": { + "version": 76, + "identityHash": "ec9a85e977d04757b8e2674970e8dc91", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ec9a85e977d04757b8e2674970e8dc91')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/77.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/77.json new file mode 100644 index 00000000..a2d13036 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/77.json @@ -0,0 +1,1791 @@ +{ + "formatVersion": 1, + "database": { + "version": 77, + "identityHash": "f14b7328eecced6a5e4af2cccc035c0a", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTransaction", + "columnName": "isTransaction", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f14b7328eecced6a5e4af2cccc035c0a')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/78.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/78.json new file mode 100644 index 00000000..0f5fbc70 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/78.json @@ -0,0 +1,1791 @@ +{ + "formatVersion": 1, + "database": { + "version": 78, + "identityHash": "f14b7328eecced6a5e4af2cccc035c0a", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `type` TEXT NOT NULL, `isReplayed` INTEGER NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_startTimeFormatted` TEXT NOT NULL, `request_byteSizeFormatted` TEXT NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_domainFormatted` TEXT NOT NULL, `request_methodFormatted` TEXT NOT NULL, `request_queryFormatted` TEXT NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `request_websocket_event` TEXT, `response_durationMs` REAL, `response_durationFormatted` TEXT, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseByteSizeFormatted` TEXT, `response_responseError` TEXT, `response_isImage` INTEGER, `response_statusFormatted` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReplayed", + "columnName": "isReplayed", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.startTimeFormatted", + "columnName": "request_startTimeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.byteSizeFormatted", + "columnName": "request_byteSizeFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.domainFormatted", + "columnName": "request_domainFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.methodFormatted", + "columnName": "request_methodFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.queryFormatted", + "columnName": "request_queryFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "request.websocket.event", + "columnName": "request_websocket_event", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.durationFormatted", + "columnName": "response_durationFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseByteSizeFormatted", + "columnName": "response_responseByteSizeFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.isImage", + "columnName": "response_isImage", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.statusFormatted", + "columnName": "response_statusFormatted", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FileOptionsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `withFoldersSize` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "withFoldersSize", + "columnName": "withFoldersSize", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "indices": [ + { + "name": "index_FileOptionsEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileOptionsEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DashboardContainerEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `containerOrder` INTEGER NOT NULL, `containerConfig` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "containerOrder", + "columnName": "containerOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerConfig", + "columnName": "containerConfig", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardContainerEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardContainerEntity_dashboardId_containerOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "containerOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardContainerEntity_dashboardId_containerOrder` ON `${TABLE_NAME}` (`dashboardId`, `containerOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `containerId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `elementAsJson` TEXT NOT NULL, FOREIGN KEY(`containerId`) REFERENCES `DashboardContainerEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "containerId", + "columnName": "containerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementAsJson", + "columnName": "elementAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_containerId", + "unique": false, + "columnNames": [ + "containerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId` ON `${TABLE_NAME}` (`containerId`)" + }, + { + "name": "index_DashboardElementEntity_containerId_elementOrder", + "unique": true, + "columnNames": [ + "containerId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_containerId_elementOrder` ON `${TABLE_NAME}` (`containerId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardContainerEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "containerId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, `headersJsonEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `url`, `time`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "headersJsonEncoded", + "columnName": "headersJsonEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "url", + "time" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "FavoriteQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `title` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FavoriteQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FavoriteQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT, `parametersAsJson` TEXT NOT NULL, `isHistory` INTEGER NOT NULL, FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + }, + { + "fieldPath": "parametersAsJson", + "columnName": "parametersAsJson", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isHistory", + "columnName": "isHistory", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `createdAtFormatted` TEXT NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAtFormatted", + "columnName": "createdAtFormatted", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkFilterEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "NetworkSettingsEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `valueAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueAsJson", + "columnName": "valueAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT, `packageName` TEXT, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT" + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, `platform` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "platform", + "columnName": "platform", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, `lastAppInstance` INTEGER NOT NULL, `floconVersionOnDevice` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`), FOREIGN KEY(`deviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + }, + { + "fieldPath": "lastAppInstance", + "columnName": "lastAppInstance", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "floconVersionOnDevice", + "columnName": "floconVersionOnDevice", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DatabaseTableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `tableName` TEXT NOT NULL, `columnsAsString` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `packageName`, `databaseId`, `tableName`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableName", + "columnName": "tableName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnsAsString", + "columnName": "columnsAsString", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "tableName" + ] + }, + "indices": [ + { + "name": "index_DatabaseTableEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DatabaseTableEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "CrashReportEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`crashId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `exceptionType` TEXT NOT NULL, `exceptionMessage` TEXT NOT NULL, `stackTrace` TEXT NOT NULL, PRIMARY KEY(`crashId`), FOREIGN KEY(`deviceId`, `packageName`) REFERENCES `DeviceAppEntity`(`deviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "crashId", + "columnName": "crashId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exceptionType", + "columnName": "exceptionType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "exceptionMessage", + "columnName": "exceptionMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "stackTrace", + "columnName": "stackTrace", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "crashId" + ] + }, + "indices": [ + { + "name": "index_CrashReportEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CrashReportEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ], + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "packageName" + ], + "referencedColumns": [ + "deviceId", + "packageName" + ] + } + ] + }, + { + "tableName": "DatabaseQueryLogEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dbName` TEXT NOT NULL, `sqlQuery` TEXT NOT NULL, `bindArgs` TEXT, `timestamp` INTEGER NOT NULL, `isTransaction` INTEGER NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `appInstance` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dbName", + "columnName": "dbName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sqlQuery", + "columnName": "sqlQuery", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bindArgs", + "columnName": "bindArgs", + "affinity": "TEXT" + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isTransaction", + "columnName": "isTransaction", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appInstance", + "columnName": "appInstance", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f14b7328eecced6a5e4af2cccc035c0a')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt index 31a9df01..29a1a3ca 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt @@ -19,6 +19,8 @@ import io.github.openflocon.data.local.database.dao.TablesDao import io.github.openflocon.data.local.database.models.DatabaseTableEntity import io.github.openflocon.data.local.database.models.FavoriteQueryEntity import io.github.openflocon.data.local.database.models.SuccessQueryEntity +import io.github.openflocon.data.local.database.dao.DatabaseQueryLogDao +import io.github.openflocon.data.local.database.models.DatabaseQueryLogEntity import io.github.openflocon.data.local.deeplink.dao.FloconDeeplinkDao import io.github.openflocon.data.local.deeplink.models.DeeplinkEntity import io.github.openflocon.data.local.device.datasource.dao.DevicesDao @@ -48,7 +50,7 @@ import io.github.openflocon.flocondesktop.common.db.converters.MapStringsConvert import kotlinx.coroutines.Dispatchers @Database( - version = 74, + version = 78, entities = [ FloconNetworkCallEntity::class, FileEntity::class, @@ -72,6 +74,7 @@ import kotlinx.coroutines.Dispatchers DeviceAppEntity::class, DatabaseTableEntity::class, CrashReportEntity::class, + DatabaseQueryLogEntity::class, ], ) @TypeConverters( @@ -96,6 +99,7 @@ abstract class AppDatabase : RoomDatabase() { abstract val devicesDao: DevicesDao abstract val tablesDao: TablesDao abstract val crashReportDao: CrashReportDao + abstract val databaseQueryLogDao: DatabaseQueryLogDao } fun getRoomDatabase(): AppDatabase = getDatabaseBuilder() diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt index d921ba74..9cbc26c8 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt @@ -55,4 +55,7 @@ val roomModule = single { get().crashReportDao } + single { + get().databaseQueryLogDao + } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DI.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DI.kt index 907105a8..557a479c 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DI.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DI.kt @@ -1,6 +1,9 @@ package io.github.openflocon.flocondesktop.features.database +import io.github.openflocon.domain.database.usecase.GetDatabaseQueryLogsUseCase import io.github.openflocon.flocondesktop.features.database.delegate.DatabaseSelectorDelegate +import io.github.openflocon.flocondesktop.features.database.processor.ExportDatabaseQueryLogsToCsvProcessor +import io.github.openflocon.flocondesktop.features.database.processor.ExportDatabaseQueryLogsToMarkdownProcessor import io.github.openflocon.flocondesktop.features.database.processor.ExportDatabaseResultToCsvProcessor import io.github.openflocon.flocondesktop.features.database.processor.ImportSqlQueryProcessor import org.koin.core.module.dsl.factoryOf @@ -13,4 +16,8 @@ internal val databaseModule = module { factoryOf(::DatabaseSelectorDelegate) factoryOf(::ImportSqlQueryProcessor) factoryOf(::ExportDatabaseResultToCsvProcessor) + factoryOf(::ExportDatabaseQueryLogsToCsvProcessor) + factoryOf(::ExportDatabaseQueryLogsToMarkdownProcessor) + factoryOf(::GetDatabaseQueryLogsUseCase) + viewModelOf(::DatabaseQueryLogsViewModel) } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt new file mode 100644 index 00000000..fb2429bc --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseQueryLogsViewModel.kt @@ -0,0 +1,205 @@ +package io.github.openflocon.flocondesktop.features.database + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.map +import io.github.openflocon.domain.common.DispatcherProvider +import io.github.openflocon.domain.common.combines +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel +import io.github.openflocon.domain.database.usecase.GetDatabaseQueryLogsUseCase +import io.github.openflocon.domain.database.usecase.ObserveDatabaseQueryLogsUseCase +import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase +import io.github.openflocon.domain.feedback.FeedbackDisplayer +import io.github.openflocon.flocondesktop.features.database.model.DatabaseQueryUiModel +import io.github.openflocon.flocondesktop.features.database.model.FilterChipUiModel +import io.github.openflocon.flocondesktop.features.database.model.toDomain +import io.github.openflocon.flocondesktop.features.database.processor.ExportDatabaseQueryLogsToCsvProcessor +import io.github.openflocon.flocondesktop.features.database.processor.ExportDatabaseQueryLogsToMarkdownProcessor +import io.github.openflocon.library.designsystem.common.copyToClipboard +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +import io.github.openflocon.domain.database.utils.injectSqlArgs + +class DatabaseQueryLogsViewModel( + private val dbName: String, + private val observeDatabaseQueryLogsUseCase: ObserveDatabaseQueryLogsUseCase, + private val getDatabaseQueryLogsUseCase: GetDatabaseQueryLogsUseCase, + private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, + private val feedbackDisplayer: FeedbackDisplayer, + private val exportDatabaseQueryLogsToCsvProcessor: ExportDatabaseQueryLogsToCsvProcessor, + private val exportDatabaseQueryLogsToMarkdownProcessor: ExportDatabaseQueryLogsToMarkdownProcessor, + dispatcherProvider: DispatcherProvider, +) : ViewModel() { + + private val _showTransactions = MutableStateFlow(false) + val showTransactions = _showTransactions.asStateFlow() + + private val _filterChips = MutableStateFlow>(emptyList()) + val filterChips = _filterChips.asStateFlow() + + private val _searchQuery = MutableStateFlow("") + val searchQuery = _searchQuery.asStateFlow() + + @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) + val logs: Flow> = + combines( + _showTransactions, + _filterChips.map { it.map { it.toDomain() } }.distinctUntilChanged(), + observeCurrentDeviceIdAndPackageNameUseCase(), + ).flatMapLatest { (showTransactions, filterChips, currentDeviceAndPackage) -> + observeDatabaseQueryLogsUseCase( + dbName = dbName, + showTransactions = showTransactions, + filters = filterChips, + ).map { + it.map { + it.toUi(currentDeviceAndPackage = currentDeviceAndPackage) + } + } + } + .flowOn(dispatcherProvider.viewModel) + .cachedIn(viewModelScope) + + fun toggleShowTransactions() { + _showTransactions.update { !it } + } + + fun onSearchQueryChanged(query: String) { + _searchQuery.value = query + } + + fun addIncludeFilter() { + val query = _searchQuery.value.trim() + if (query.isNotEmpty() && !_filterChips.value.any { it.text == query }) { + _filterChips.update { it + FilterChipUiModel(query, FilterChipUiModel.FilterType.INCLUDE) } + _searchQuery.value = "" + } + } + + fun addExcludeFilter() { + val query = _searchQuery.value.trim() + if (query.isNotEmpty() && !_filterChips.value.any { it.text == query }) { + _filterChips.update { it + FilterChipUiModel(query, FilterChipUiModel.FilterType.EXCLUDE) } + _searchQuery.value = "" + } + } + + fun toggleFilterType(chip: FilterChipUiModel) { + _filterChips.update { list -> + list.map { + if (it == chip) { + it.copy(type = if (it.type == FilterChipUiModel.FilterType.INCLUDE) + FilterChipUiModel.FilterType.EXCLUDE + else + FilterChipUiModel.FilterType.INCLUDE + ) + } else { + it + } + } + } + } + + fun addFilter(text: String, type: FilterChipUiModel.FilterType) { + if (text.isNotEmpty() && !_filterChips.value.any { it.text == text }) { + _filterChips.update { it + FilterChipUiModel(text, type) } + } + } + + fun removeFilterChip(chip: FilterChipUiModel) { + _filterChips.update { it - chip } + } + + fun copyQuery(query: String) { + copyToClipboard(query) + feedbackDisplayer.displayMessage("Query copied to clipboard") + } + + fun copyArgs(args: List?) { + val argsString = args?.toString() ?: "[]" + copyToClipboard(argsString) + feedbackDisplayer.displayMessage("Arguments copied to clipboard") + } + + fun copyAsSql(query: String, args: List?) { + val fullSql = injectSqlArgs(query, args) + copyToClipboard(fullSql) + feedbackDisplayer.displayMessage("SQL with arguments copied to clipboard") + } + + fun exportToCsv() { + viewModelScope.launch { + val logs = getDatabaseQueryLogsUseCase( + dbName = dbName, + showTransactions = _showTransactions.value, + filters = _filterChips.value.map { it.toDomain() } + ) + exportDatabaseQueryLogsToCsvProcessor(logs).fold( + doOnSuccess = { + feedbackDisplayer.displayMessage("Logs exported to $it") + }, + doOnFailure = { + feedbackDisplayer.displayMessage("Export failed: ${it.message}") + } + ) + } + } + + fun exportToMarkdown() { + viewModelScope.launch { + val logs = getDatabaseQueryLogsUseCase( + dbName = dbName, + showTransactions = _showTransactions.value, + filters = _filterChips.value.map { it.toDomain() } + ) + exportDatabaseQueryLogsToMarkdownProcessor(logs).fold( + doOnSuccess = { + feedbackDisplayer.displayMessage("Logs exported to $it") + }, + doOnFailure = { + feedbackDisplayer.displayMessage("Export failed: ${it.message}") + } + ) + } + } +} + +private fun DatabaseQueryLogDomainModel.toUi(currentDeviceAndPackage: DeviceIdAndPackageNameDomainModel?) = DatabaseQueryUiModel( + sqlQuery = sqlQuery, + bindArgs = bindArgs, + dateFormatted = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date(timestamp)), + isTransaction = isTransaction, + isFromOldSession = currentDeviceAndPackage?.appInstance != appInstance, + type = findType(this), +) + + +private fun findType(model: DatabaseQueryLogDomainModel): DatabaseQueryUiModel.Type? { + return if(model.isTransaction) { + DatabaseQueryUiModel.Type.Transaction + } else if(model.sqlQuery.startsWith("SELECT")) { + DatabaseQueryUiModel.Type.Select + } else if(model.sqlQuery.startsWith("INSERT")) { + DatabaseQueryUiModel.Type.Insert + } else if(model.sqlQuery.startsWith("UPDATE")) { + DatabaseQueryUiModel.Type.Update + } else if(model.sqlQuery.startsWith("DELETE")) { + DatabaseQueryUiModel.Type.Delete + } else { + null + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt index 422c6596..e81b2aa6 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/DatabaseViewModel.kt @@ -135,6 +135,11 @@ class DatabaseViewModel( table = action.table, query = generateInsertQuery(action.table) ) + + is DatabaseScreenAction.OnSeeAllQueriesClicked -> createTabForQueryLogs( + databaseId = action.databaseId, + dbName = action.dbName + ) } } } @@ -154,6 +159,7 @@ class DatabaseViewModel( val generatedName = table.name createTab( databaseId = databaseId, + databaseName = "", // not used here tableName = null, generatedName = generatedName, favoriteId = null, @@ -161,6 +167,18 @@ class DatabaseViewModel( ) } + private suspend fun createTabForQueryLogs(databaseId: DeviceDataBaseId, dbName: String) { + createTab( + databaseId = databaseId, + databaseName = dbName, // only used here + tableName = null, + generatedName = "Logs ($dbName)", + favoriteId = null, + query = null, + isQueryLogs = true, + ) + } + private fun onTableColumnClicked(column: TableUiModel.ColumnUiModel) { copyToClipboard(column.name) feedbackDisplayer.displayMessage("copied: ${column.name}") @@ -275,6 +293,8 @@ class DatabaseViewModel( favoriteId: Long?, generatedName: String, query: String?, + isQueryLogs: Boolean = false, + databaseName: String? = null, // only used for isQueryLogs ) { val deviceIdAndPackageName = getCurrentDeviceIdAndPackageNameUseCase() ?: return @@ -291,12 +311,14 @@ class DatabaseViewModel( val addedTab = DatabaseTabState( databaseId = databaseId, + databaseName = databaseName ?: "", tableName = tableName, generatedName = generatedName, index = index, favoriteId = favoriteId, query = query, id = UUID.randomUUID().toString(), + isQueryLogs = isQueryLogs, ) val newList = list.toMutableList().apply { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseQueryUiModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseQueryUiModel.kt new file mode 100644 index 00000000..cfa933f4 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseQueryUiModel.kt @@ -0,0 +1,21 @@ +package io.github.openflocon.flocondesktop.features.database.model + +import androidx.compose.runtime.Immutable + +@Immutable +data class DatabaseQueryUiModel( + val sqlQuery: String, + val bindArgs: List?, + val dateFormatted: String, + val isTransaction: Boolean, + val isFromOldSession: Boolean, + val type: Type?, +) { + enum class Type { + Select, + Insert, + Update, + Delete, + Transaction, + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseScreenAction.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseScreenAction.kt index 4b13ffe5..7a5a7523 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseScreenAction.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseScreenAction.kt @@ -12,4 +12,5 @@ sealed interface DatabaseScreenAction { data class DeleteFavorite(val favoriteQuery: DatabaseFavoriteQueryUiModel) : DatabaseScreenAction data class OnDeleteContentClicked(val databaseId: DeviceDataBaseId, val table: TableUiModel) : DatabaseScreenAction data class OnInsertContentClicked(val databaseId: DeviceDataBaseId, val table: TableUiModel) : DatabaseScreenAction + data class OnSeeAllQueriesClicked(val databaseId: DeviceDataBaseId, val dbName: String) : DatabaseScreenAction } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt index 94550191..984e47de 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/DatabaseTabState.kt @@ -5,11 +5,13 @@ import androidx.compose.runtime.Immutable @Immutable data class DatabaseTabState( val databaseId: String, + val databaseName: String, val tableName: String?, val generatedName: String, val index: Int, val favoriteId: Long?, val query: String?, + val isQueryLogs: Boolean = false, val id: String, ) { val displayName: String = generatedName + if (index > 0) " ($index)" else "" diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/FilterChipModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/FilterChipModel.kt new file mode 100644 index 00000000..68e42347 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/model/FilterChipModel.kt @@ -0,0 +1,23 @@ +package io.github.openflocon.flocondesktop.features.database.model + +import androidx.compose.runtime.Immutable +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel + +@Immutable +data class FilterChipUiModel( + val text: String, + val type: FilterType +) { + enum class FilterType { + INCLUDE, + EXCLUDE + } +} + +fun FilterChipUiModel.toDomain() = FilterQueryLogDomainModel( + text = text, + type = when (type) { + FilterChipUiModel.FilterType.INCLUDE -> FilterQueryLogDomainModel.FilterType.INCLUDE + FilterChipUiModel.FilterType.EXCLUDE -> FilterQueryLogDomainModel.FilterType.EXCLUDE + } +) \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToCsvProcessor.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToCsvProcessor.kt new file mode 100644 index 00000000..910c481d --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToCsvProcessor.kt @@ -0,0 +1,84 @@ +package io.github.openflocon.flocondesktop.features.database.processor + +import io.github.openflocon.domain.common.Either +import io.github.openflocon.domain.common.Failure +import io.github.openflocon.domain.common.Success +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel +import io.github.openflocon.domain.database.models.toFullSql +import java.awt.FileDialog +import java.awt.Frame +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class ExportDatabaseQueryLogsToCsvProcessor { + + suspend operator fun invoke( + logs: List + ): Either { + val fileName = "database_logs_${System.currentTimeMillis()}.csv" + + val file = showSaveFileDialog(defaultFileName = fileName, dialogName = "Export database logs as CSV") ?: return Failure( + Throwable("no file selected") + ) + + exportToCsv( + file = file, + logs = logs, + ) + return Success(file.absolutePath) + } + + private fun showSaveFileDialog(dialogName: String, defaultFileName: String): File? { + val parentFrame = Frame() + val dialog = FileDialog(parentFrame, dialogName, FileDialog.SAVE).apply { + file = defaultFileName + } + + dialog.isVisible = true + + val file = dialog.file + val directory = dialog.directory + + parentFrame.dispose() + + return if (file != null && directory != null) { + File(directory, file) + } else { + null + } + } + + private fun exportToCsv( + file: File, + logs: List, + ) { + val columns = listOf("Date", "SQL Query", "Arguments", "Full SQL") + file.writeText(columns.joinToString(separator = ",", postfix = "\n")) + + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + + logs.forEach { log -> + val row = listOf( + dateFormat.format(Date(log.timestamp)), + log.sqlQuery, + log.bindArgs?.toString() ?: "[]", + log.toFullSql() + ) + val escapedRow = row.map { csvEscape(it) } + file.appendText(escapedRow.joinToString(separator = ",", postfix = "\n")) + } + } + + private fun csvEscape(text: String?): String { + val nonNullText = text ?: "" + val containsSpecialChars = nonNullText.contains(',') || nonNullText.contains('"') || nonNullText.contains('\n') + return if (containsSpecialChars) { + val escapedText = nonNullText.replace("\"", "\"\"") + "\"$escapedText\"" + } else { + nonNullText + } + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToMarkdownProcessor.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToMarkdownProcessor.kt new file mode 100644 index 00000000..ed92fe63 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/processor/ExportDatabaseQueryLogsToMarkdownProcessor.kt @@ -0,0 +1,84 @@ +package io.github.openflocon.flocondesktop.features.database.processor + +import io.github.openflocon.domain.common.Either +import io.github.openflocon.domain.common.Failure +import io.github.openflocon.domain.common.Success +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel +import io.github.openflocon.domain.database.models.toFullSql +import java.awt.FileDialog +import java.awt.Frame +import java.io.File +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class ExportDatabaseQueryLogsToMarkdownProcessor { + + suspend operator fun invoke( + logs: List + ): Either { + val fileName = "database_logs_${System.currentTimeMillis()}.md" + + val file = showSaveFileDialog(defaultFileName = fileName, dialogName = "Export database logs as Markdown") ?: return Failure( + Throwable("no file selected") + ) + + exportToMarkdown( + file = file, + logs = logs, + ) + return Success(file.absolutePath) + } + + private fun showSaveFileDialog(dialogName: String, defaultFileName: String): File? { + val parentFrame = Frame() + val dialog = FileDialog(parentFrame, dialogName, FileDialog.SAVE).apply { + file = defaultFileName + } + + dialog.isVisible = true + + val file = dialog.file + val directory = dialog.directory + + parentFrame.dispose() + + return if (file != null && directory != null) { + File(directory, file) + } else { + null + } + } + + private fun exportToMarkdown( + file: File, + logs: List, + ) { + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + + val markdown = buildString { + logs.forEach { log -> + val date = dateFormat.format(Date(log.timestamp)) + val sql = log.sqlQuery.replace("|", "\\|").replace("\n", " ") + val args = (log.bindArgs?.toString() ?: "[]").replace("|", "\\|") + val fullSql = log.toFullSql().replace("|", "\\|").replace("\n", " ") + + appendLine("# $date") + appendLine() + appendLine("### query") + appendLine(sql) + appendLine() + appendLine("### args") + appendLine(args) + appendLine() + appendLine("### SQL") + appendLine(fullSql) + appendLine() + appendLine("--------------------------------------------------------------------------------") + appendLine() + } + } + + file.writeText(markdown) + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt index b7152e1e..f45127d8 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseQueryView.kt @@ -154,9 +154,6 @@ fun DatabaseQueryView( isQueryEmpty = query.isBlank(), ) - val highlightedText = remember(query) { - } - FloconTextField( value = query, onValueChange = updateQuery, diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt index bc42044f..2369edc1 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTab.kt @@ -1,108 +1,25 @@ package io.github.openflocon.flocondesktop.features.database.view -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import io.github.openflocon.flocondesktop.features.database.DatabaseTabViewModel -import io.github.openflocon.flocondesktop.features.database.model.DatabaseScreenState -import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabAction import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabState -import io.github.openflocon.library.designsystem.FloconTheme -import io.github.openflocon.library.designsystem.components.FloconPageTopBar -import org.koin.compose.viewmodel.koinViewModel -import org.koin.core.parameter.parametersOf +import io.github.openflocon.flocondesktop.features.database.view.logs.DatabaseQueryLogsView @Composable fun DatabaseTabView( tab: DatabaseTabState, favoritesTitles: Set, ) { - val viewModel: DatabaseTabViewModel = koinViewModel( - key = tab.id, - parameters = { - parametersOf( - DatabaseTabViewModel.Params( - databaseId = tab.databaseId, - tableName = tab.tableName, - favoriteId = tab.favoriteId, - query = tab.query, - ) - ) - } - ) - - DisposableEffect(viewModel) { - viewModel.onVisible() - onDispose { - viewModel.onNotVisible() - } - } - - val state: DatabaseScreenState by viewModel.state.collectAsStateWithLifecycle() - val autoUpdate by viewModel.isAutoUpdateEnabled.collectAsStateWithLifecycle() - val lastQueries by viewModel.lastQueries.collectAsStateWithLifecycle() - - DatabaseTabViewContent( - query = viewModel.query.value, - autoUpdate = autoUpdate, - updateQuery = viewModel::updateQuery, - onAction = viewModel::onAction, - state = state, - lastQueries = lastQueries, - favoritesTitles = favoritesTitles, - ) -} - -@Composable -private fun DatabaseTabViewContent( - query: String, - autoUpdate: Boolean, - favoritesTitles: Set, - updateQuery: (String) -> Unit, - onAction: (action: DatabaseTabAction) -> Unit, - state: DatabaseScreenState, - lastQueries: List, -) { - Column( - Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - FloconPageTopBar( - modifier = Modifier.fillMaxWidth() - .clip(FloconTheme.shapes.medium) - .border( - width = 1.dp, - color = FloconTheme.colorPalette.secondary, - shape = FloconTheme.shapes.medium - ), - ) { contentPadding -> - DatabaseQueryView( - query = query, - updateQuery = updateQuery, - autoUpdate = autoUpdate, - onAction = onAction, - favoritesTitles = favoritesTitles, - lastQueries = lastQueries, - modifier = Modifier - .fillMaxWidth() - ) - } - - DatabaseContentView( - state = state, - modifier = Modifier.fillMaxWidth(), - onExportCsvClicked = { - onAction(DatabaseTabAction.ExportCsv) - }, + if (tab.isQueryLogs) { + DatabaseQueryLogsView( + dbName = tab.databaseName, + modifier = Modifier.fillMaxSize() + ) + } else { + DatabaseTabQueryView( + tab = tab, + favoritesTitles = favoritesTitles, ) } -} +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTabQueryView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTabQueryView.kt new file mode 100644 index 00000000..616d0975 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/DatabaseTabQueryView.kt @@ -0,0 +1,110 @@ +package io.github.openflocon.flocondesktop.features.database.view + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import io.github.openflocon.flocondesktop.features.database.DatabaseTabViewModel +import io.github.openflocon.flocondesktop.features.database.model.DatabaseScreenState +import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabAction +import io.github.openflocon.flocondesktop.features.database.model.DatabaseTabState +import io.github.openflocon.library.designsystem.FloconTheme +import io.github.openflocon.library.designsystem.components.FloconPageTopBar +import org.koin.compose.viewmodel.koinViewModel +import org.koin.core.parameter.parametersOf + +@Composable +fun DatabaseTabQueryView( + tab: DatabaseTabState, + favoritesTitles: Set, +) { + val viewModel: DatabaseTabViewModel = koinViewModel( + key = tab.id, + parameters = { + parametersOf( + DatabaseTabViewModel.Params( + databaseId = tab.databaseId, + tableName = tab.tableName, + favoriteId = tab.favoriteId, + query = tab.query, + ) + ) + } + ) + + DisposableEffect(viewModel) { + viewModel.onVisible() + onDispose { + viewModel.onNotVisible() + } + } + + val state: DatabaseScreenState by viewModel.state.collectAsStateWithLifecycle() + val autoUpdate by viewModel.isAutoUpdateEnabled.collectAsStateWithLifecycle() + val lastQueries by viewModel.lastQueries.collectAsStateWithLifecycle() + + DatabaseTabViewContent( + query = viewModel.query.value, + autoUpdate = autoUpdate, + updateQuery = viewModel::updateQuery, + onAction = viewModel::onAction, + state = state, + lastQueries = lastQueries, + favoritesTitles = favoritesTitles, + ) +} + + + +@Composable +private fun DatabaseTabViewContent( + query: String, + autoUpdate: Boolean, + favoritesTitles: Set, + updateQuery: (String) -> Unit, + onAction: (action: DatabaseTabAction) -> Unit, + state: DatabaseScreenState, + lastQueries: List, +) { + Column( + Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + FloconPageTopBar( + modifier = Modifier.fillMaxWidth() + .clip(FloconTheme.shapes.medium) + .border( + width = 1.dp, + color = FloconTheme.colorPalette.secondary, + shape = FloconTheme.shapes.medium + ), + ) { contentPadding -> + DatabaseQueryView( + query = query, + updateQuery = updateQuery, + autoUpdate = autoUpdate, + onAction = onAction, + favoritesTitles = favoritesTitles, + lastQueries = lastQueries, + modifier = Modifier + .fillMaxWidth() + ) + } + + DatabaseContentView( + state = state, + modifier = Modifier.fillMaxWidth(), + onExportCsvClicked = { + onAction(DatabaseTabAction.ExportCsv) + }, + ) + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt index df6d9938..e835b9b0 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabaseItemView.kt @@ -1,8 +1,13 @@ +@file:OptIn(ExperimentalFoundationApi::class) + package io.github.openflocon.flocondesktop.features.database.view.databases_tables +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -12,6 +17,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Dataset +import androidx.compose.material.icons.outlined.Preview +import androidx.compose.material.icons.outlined.Visibility import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -23,9 +30,12 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach import io.github.openflocon.domain.database.models.DeviceDataBaseId +import io.github.openflocon.flocondesktop.common.ui.ContextualView import io.github.openflocon.flocondesktop.features.database.model.DeviceDataBaseUiModel import io.github.openflocon.flocondesktop.features.database.model.TableUiModel import io.github.openflocon.library.designsystem.FloconTheme +import io.github.openflocon.library.designsystem.common.FloconContextMenuItem +import io.github.openflocon.library.designsystem.components.WithTooltip @Composable internal fun DatabaseItemView( @@ -36,6 +46,7 @@ internal fun DatabaseItemView( onTableColumnClicked: (TableUiModel.ColumnUiModel) -> Unit, onDeleteContentClicked: (databaseId: DeviceDataBaseId, TableUiModel) -> Unit, onInsertContentClicked: (databaseId: DeviceDataBaseId, TableUiModel) -> Unit, + onSeeAllQueriesClicked: (DeviceDataBaseId, String) -> Unit, modifier: Modifier = Modifier, ) { Column(modifier = modifier) { @@ -44,6 +55,7 @@ internal fun DatabaseItemView( onSelect = onSelect, state = state, onDatabaseDoubleClicked = onDatabaseDoubleClicked, + onSeeAllQueriesClicked = onSeeAllQueriesClicked, ) state.tables?.let { tables -> Column(modifier = Modifier.fillMaxWidth()) { @@ -73,6 +85,7 @@ private fun DatabaseView( onSelect: (DeviceDataBaseId) -> Unit, state: DeviceDataBaseUiModel, onDatabaseDoubleClicked: (DeviceDataBaseUiModel) -> Unit, + onSeeAllQueriesClicked: (DeviceDataBaseId, String) -> Unit, modifier: Modifier = Modifier, ) { val (background, textColor) = if (state.isSelected) { @@ -81,32 +94,58 @@ private fun DatabaseView( Color.Transparent to FloconTheme.colorPalette.onSurface } - Row( - modifier = modifier - .clip(RoundedCornerShape(8.dp)) - .background(background) - .combinedClickable( - onClick = { - onSelect(state.id) - }, onDoubleClick = { - onDatabaseDoubleClicked(state) - } - ).padding(horizontal = 12.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically, + ContextualView( + listOf( + FloconContextMenuItem.Item( + label = "See all queries", + onClick = { + onSeeAllQueriesClicked(state.id, state.name) + } + )) ) { - Image( - modifier = Modifier.width(14.dp), - imageVector = Icons.Outlined.Dataset, - contentDescription = null, - colorFilter = ColorFilter.tint(textColor), - ) - Spacer(modifier = Modifier.width(4.dp)) - Text( - state.name, - color = textColor, - style = FloconTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + Row( + modifier = modifier + .clip(RoundedCornerShape(8.dp)) + .background(background) + .combinedClickable( + onClick = { + onSelect(state.id) + }, onDoubleClick = { + onDatabaseDoubleClicked(state) + } + ).padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + modifier = Modifier.width(14.dp), + imageVector = Icons.Outlined.Dataset, + contentDescription = null, + colorFilter = ColorFilter.tint(textColor), + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + state.name, + color = textColor, + style = FloconTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + WithTooltip("See logs") { + Box( + modifier = Modifier.width(20.dp) + .clickable(onClick = { + onSeeAllQueriesClicked(state.id, state.name) + }) + ) { + Image( + modifier = Modifier.width(16.dp), + imageVector = Icons.Outlined.Preview, + contentDescription = null, + colorFilter = ColorFilter.tint(textColor), + ) + } + } + } } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt index 88bf77a5..dbbda17a 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/databases_tables/DatabasesAndTablesView.kt @@ -89,6 +89,9 @@ fun DatabasesAndTablesView( }, onInsertContentClicked = { id, table -> onAction(DatabaseScreenAction.OnInsertContentClicked(id, table)) + }, + onSeeAllQueriesClicked = { id, dbName -> + onAction(DatabaseScreenAction.OnSeeAllQueriesClicked(id, dbName)) } ) } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogItemView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogItemView.kt new file mode 100644 index 00000000..96c1e9dd --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogItemView.kt @@ -0,0 +1,65 @@ +package io.github.openflocon.flocondesktop.features.database.view.logs + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import io.github.openflocon.flocondesktop.features.database.model.DatabaseQueryUiModel +import io.github.openflocon.library.designsystem.FloconTheme + +@Composable +fun DatabaseQueryLogItemView( + log: DatabaseQueryUiModel, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier.then( + if (log.isFromOldSession) { + Modifier.alpha(0.8f) + } else Modifier + ), horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + modifier = Modifier.width(60.dp), + text = log.dateFormatted, + style = FloconTheme.typography.bodySmall, + textAlign = TextAlign.Center, + color = FloconTheme.colorPalette.onSurface + .copy(alpha = 0.6f) + ) + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + Text( + text = log.sqlQuery, + style = FloconTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace), + color = findColor(log.type) + ) + log.bindArgs?.let { + Text( + text = "Args: ${log.bindArgs}", + style = FloconTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace), + color = FloconTheme.colorPalette.onSurface.copy(alpha = 0.8f) + ) + } + } + } +} + +@Composable +private fun findColor(type: DatabaseQueryUiModel.Type?): Color { + return when (type) { + DatabaseQueryUiModel.Type.Select -> Color(0xFF28A745) + DatabaseQueryUiModel.Type.Insert -> Color(0xFF007BFF) + DatabaseQueryUiModel.Type.Update -> Color(0xFFFFC107) + DatabaseQueryUiModel.Type.Delete -> Color(0xFFDC3545) + DatabaseQueryUiModel.Type.Transaction, + null -> FloconTheme.colorPalette.onSurface + } +} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogsView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogsView.kt new file mode 100644 index 00000000..70decbc2 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/view/logs/DatabaseQueryLogsView.kt @@ -0,0 +1,304 @@ +package io.github.openflocon.flocondesktop.features.database.view.logs + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material.icons.outlined.Download +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material.icons.outlined.Upload +import androidx.compose.material.icons.outlined.UploadFile +import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.InputChip +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.paging.compose.collectAsLazyPagingItems +import io.github.openflocon.flocondesktop.common.ui.ContextualView +import io.github.openflocon.flocondesktop.features.database.DatabaseQueryLogsViewModel +import io.github.openflocon.flocondesktop.features.database.model.FilterChipUiModel +import io.github.openflocon.flocondesktop.features.network.list.model.NetworkAction +import io.github.openflocon.library.designsystem.FloconTheme +import io.github.openflocon.library.designsystem.common.FloconContextMenuItem +import io.github.openflocon.library.designsystem.components.FloconDropdownMenuItem +import io.github.openflocon.library.designsystem.components.FloconIcon +import io.github.openflocon.library.designsystem.components.FloconIconButton +import io.github.openflocon.library.designsystem.components.FloconIconToggleButton +import io.github.openflocon.library.designsystem.components.FloconOverflow +import io.github.openflocon.library.designsystem.components.FloconPageTopBar +import io.github.openflocon.library.designsystem.components.FloconTextFieldWithoutM3 +import io.github.openflocon.library.designsystem.components.defaultPlaceHolder +import org.koin.compose.viewmodel.koinViewModel +import org.koin.core.parameter.parametersOf + +@Composable +fun DatabaseQueryLogsView( + dbName: String, + modifier: Modifier = Modifier +) { + val viewModel: DatabaseQueryLogsViewModel = koinViewModel( + key = dbName, + parameters = { parametersOf(dbName) } + ) + + val logs = viewModel.logs.collectAsLazyPagingItems() + val showTransactions by viewModel.showTransactions.collectAsStateWithLifecycle() + val filterChips by viewModel.filterChips.collectAsStateWithLifecycle() + val searchQuery by viewModel.searchQuery.collectAsStateWithLifecycle() + + Column(modifier = modifier) { + DatabaseLogsHeader( + modifier = Modifier.fillMaxWidth().border( + width = 1.dp, + color = FloconTheme.colorPalette.secondary, + shape = FloconTheme.shapes.medium + ), + searchQuery = searchQuery, + onSearchQueryChanged = viewModel::onSearchQueryChanged, + toggleShowTransactions = viewModel::toggleShowTransactions, + removeFilterChip = viewModel::removeFilterChip, + addIncludeFilter = viewModel::addIncludeFilter, + addExcludeFilter = viewModel::addExcludeFilter, + toggleFilterType = viewModel::toggleFilterType, + showTransactions = showTransactions, + filterChips = filterChips, + exportToCsv = viewModel::exportToCsv, + exportToMarkdown = viewModel::exportToMarkdown, + ) + + Box( + modifier = Modifier.fillMaxSize().padding(top = 12.dp) + .clip(FloconTheme.shapes.medium) + .border( + width = 1.dp, + color = FloconTheme.colorPalette.secondary, + shape = FloconTheme.shapes.medium + ) + .background( + color = FloconTheme.colorPalette.primary, + shape = FloconTheme.shapes.medium + ) + ) { + LazyColumn(modifier = Modifier.fillMaxSize()) { + items(logs.itemCount) { index -> + val log = logs[index] + if (log != null) { + ContextualView( + items = listOf( + FloconContextMenuItem.Item("Copy Query") { + viewModel.copyQuery(log.sqlQuery) + }, + FloconContextMenuItem.Item("Copy Args") { + viewModel.copyArgs(log.bindArgs) + }, + FloconContextMenuItem.Item("Copy as SQL") { + viewModel.copyAsSql(log.sqlQuery, log.bindArgs) + }, + FloconContextMenuItem.Separator(), + FloconContextMenuItem.Item("Filter In") { + viewModel.addFilter( + log.sqlQuery, + FilterChipUiModel.FilterType.INCLUDE + ) + }, + FloconContextMenuItem.Item("Filter Out") { + viewModel.addFilter( + log.sqlQuery, + FilterChipUiModel.FilterType.EXCLUDE + ) + } + ) + ) { + DatabaseQueryLogItemView( + log = log, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 8.dp) + ) + } + HorizontalDivider(color = FloconTheme.colorPalette.secondary) + } + } + } + } + } +} + +@Composable +private fun DatabaseLogsHeader( + searchQuery: String, + showTransactions: Boolean, + onSearchQueryChanged: (String) -> Unit, + addIncludeFilter: () -> Unit, + addExcludeFilter: () -> Unit, + toggleShowTransactions: () -> Unit, + toggleFilterType: (FilterChipUiModel) -> Unit, + removeFilterChip: (FilterChipUiModel) -> Unit, + exportToCsv: () -> Unit, + exportToMarkdown: () -> Unit, + filterChips: List, + modifier: Modifier = Modifier, +) { + FloconPageTopBar(modifier) { + Column(Modifier.fillMaxWidth()) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Row( + modifier = Modifier.weight(1f), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + FloconTextFieldWithoutM3( + value = searchQuery, + onValueChange = onSearchQueryChanged, + modifier = Modifier.weight(1f), + placeholder = { defaultPlaceHolder("Filter logs...") }, + containerColor = FloconTheme.colorPalette.secondary, + singleLine = true, + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + keyboardActions = KeyboardActions( + onDone = { + addIncludeFilter() + } + ), + leadingComponent = { + FloconIcon( + imageVector = Icons.Outlined.Search, + modifier = Modifier.size(16.dp) + ) + }, + ) + + androidx.compose.material3.IconButton(onClick = addIncludeFilter) { + Icon(imageVector = Icons.Default.Add, contentDescription = "Include") + } + androidx.compose.material3.IconButton(onClick = addExcludeFilter) { + Icon(imageVector = Icons.Default.Remove, contentDescription = "Exclude") + } + } + + FilterChip( + selected = !showTransactions, + onClick = { + toggleShowTransactions() + }, + label = { + Text("Hide transactions", style = FloconTheme.typography.bodySmall) + }, + leadingIcon = { + if (!showTransactions) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = null + ) + } + } + ) + + FloconOverflow { + FloconDropdownMenuItem( + text = "Export CSV", + leadingIcon = Icons.Outlined.Upload, + onClick = { exportToCsv() } + ) + FloconDropdownMenuItem( + text = "Export Markdown", + leadingIcon = Icons.Outlined.UploadFile, + onClick = { exportToMarkdown() } + ) + } + + } + FilterChips( + modifier = Modifier.fillMaxWidth() + .padding(top = 8.dp), + filterChips = filterChips, + toggleFilterType = toggleFilterType, + removeFilterChip = removeFilterChip, + ) + } + } +} + +@Composable +private fun FilterChips( + modifier: Modifier = Modifier, + filterChips: List, + toggleFilterType: (FilterChipUiModel) -> Unit, + removeFilterChip: (FilterChipUiModel) -> Unit +) { + val itemsColor = FloconTheme.colorPalette.onSecondary + if (filterChips.isNotEmpty()) { + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = modifier, + ) { + filterChips.fastForEach { chip -> + InputChip( + selected = true, + onClick = { toggleFilterType(chip) }, + shape = RoundedCornerShape(20.dp), + label = { + Text( + chip.text, + style = FloconTheme.typography.bodySmall, + color = itemsColor, + ) + }, + leadingIcon = { + Icon( + imageVector = if (chip.type == FilterChipUiModel.FilterType.INCLUDE) Icons.Default.Add else Icons.Default.Remove, + contentDescription = null, + tint = itemsColor, + modifier = Modifier.size(14.dp) + ) + }, + trailingIcon = { + Icon( + imageVector = Icons.Default.Close, + tint = itemsColor, + contentDescription = "Remove", + modifier = Modifier.clip(CircleShape).size(14.dp) + .clickable { + removeFilterChip(chip) + } + ) + } + ) + } + } + } +} diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt index e2b7f2d8..9caef8e3 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/LocalDatabaseDataSource.kt @@ -1,9 +1,12 @@ package io.github.openflocon.data.core.database.datasource +import androidx.paging.PagingData import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.database.models.DatabaseFavoriteQueryDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DatabaseTableDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import kotlinx.coroutines.flow.Flow @@ -58,4 +61,23 @@ interface LocalDatabaseDataSource { databaseId: String, id: Long ): DatabaseFavoriteQueryDomainModel? + + suspend fun saveQueryLog( + log: DatabaseQueryLogDomainModel, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ) + + fun observeQueryLogs( + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + dbName: String, + showTransactions: Boolean, + filters: List + ): Flow> + + suspend fun getQueryLogs( + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + dbName: String, + showTransactions: Boolean, + filters: List + ): List } diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/QueryDatabaseRemoteDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/QueryDatabaseRemoteDataSource.kt index 04dee872..b09562e0 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/QueryDatabaseRemoteDataSource.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/datasource/QueryDatabaseRemoteDataSource.kt @@ -2,6 +2,7 @@ package io.github.openflocon.data.core.database.datasource import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.database.models.DatabaseExecuteSqlResponseDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId import io.github.openflocon.domain.database.models.ResponseAndRequestIdDomainModel @@ -23,4 +24,6 @@ interface QueryDatabaseRemoteDataSource { fun getDeviceDatabases(message: FloconIncomingMessageDomainModel): List fun getReceiveQuery(message: FloconIncomingMessageDomainModel): ResponseAndRequestIdDomainModel? + + fun getQueryLogs(message: FloconIncomingMessageDomainModel): DatabaseQueryLogDomainModel? } diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt index 4d6339fb..6b9e315f 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/database/repository/DatabaseRepositoryImpl.kt @@ -1,5 +1,6 @@ package io.github.openflocon.data.core.database.repository +import androidx.paging.PagingData import io.github.openflocon.data.core.database.datasource.DeviceDatabasesRemoteDataSource import io.github.openflocon.data.core.database.datasource.LocalDatabaseDataSource import io.github.openflocon.data.core.database.datasource.QueryDatabaseRemoteDataSource @@ -8,9 +9,11 @@ import io.github.openflocon.domain.common.DispatcherProvider import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.database.models.DatabaseExecuteSqlResponseDomainModel import io.github.openflocon.domain.database.models.DatabaseFavoriteQueryDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DatabaseTableDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel import io.github.openflocon.domain.database.repository.DatabaseRepository import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel @@ -91,6 +94,17 @@ class DatabaseRepositoryImpl( databases = items, ) } + + Protocol.FromDevice.Database.Method.LogQuery -> { + queryDatabaseDataSource.getQueryLogs( + message, + )?.let { + localDatabaseDataSource.saveQueryLog( + it, + deviceIdAndPackageName = deviceIdAndPackageName + ) + } + } } } } @@ -191,4 +205,32 @@ class DatabaseRepositoryImpl( id = id, ) } + + override fun observeQueryLogs( + dbName: String, + showTransactions: Boolean, + filters: List, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ): Flow> { + return localDatabaseDataSource.observeQueryLogs( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = deviceIdAndPackageName, + ) + } + + override suspend fun getQueryLogs( + dbName: String, + showTransactions: Boolean, + filters: List, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ): List { + return localDatabaseDataSource.getQueryLogs( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = deviceIdAndPackageName, + ) + } } diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/dao/DatabaseQueryLogDao.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/dao/DatabaseQueryLogDao.kt new file mode 100644 index 00000000..d9e7a3e4 --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/dao/DatabaseQueryLogDao.kt @@ -0,0 +1,23 @@ +package io.github.openflocon.data.local.database.dao + +import androidx.paging.PagingSource +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.RoomRawQuery +import io.github.openflocon.data.local.database.models.DatabaseQueryLogEntity + +@Dao +interface DatabaseQueryLogDao { + @Insert + suspend fun insert(entity: DatabaseQueryLogEntity) + + @androidx.room.RawQuery(observedEntities = [DatabaseQueryLogEntity::class]) + fun getPagingSource( + query: RoomRawQuery, + ): PagingSource + + @androidx.room.RawQuery(observedEntities = [DatabaseQueryLogEntity::class]) + suspend fun getLogs( + query: RoomRawQuery, + ): List +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt index 5166b60e..50f0b8e6 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/datasource/LocalDatabaseDataSourceRoom.kt @@ -1,26 +1,37 @@ package io.github.openflocon.data.local.database.datasource +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import io.github.openflocon.data.core.database.datasource.LocalDatabaseDataSource +import io.github.openflocon.data.local.database.dao.DatabaseQueryLogDao import io.github.openflocon.data.local.database.dao.QueryDao import io.github.openflocon.data.local.database.dao.TablesDao import io.github.openflocon.data.local.database.mapper.toDomain import io.github.openflocon.data.local.database.mapper.toEntity +import androidx.paging.map +import androidx.room.RoomRawQuery +import io.github.openflocon.data.local.database.models.DatabaseQueryLogEntity import io.github.openflocon.data.local.database.models.FavoriteQueryEntity import io.github.openflocon.data.local.database.models.SuccessQueryEntity import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.common.Success import io.github.openflocon.domain.database.models.DatabaseFavoriteQueryDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DatabaseTableDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.serialization.json.Json +import kotlin.let internal class LocalDatabaseDataSourceRoom( private val queryDao: QueryDao, private val tablesDao: TablesDao, + private val databaseQueryLogDao: DatabaseQueryLogDao, private val json: Json, ) : LocalDatabaseDataSource { @@ -172,4 +183,146 @@ internal class LocalDatabaseDataSourceRoom( packageName = deviceIdAndPackageName.packageName, id = id, )?.toDomain() + + override suspend fun saveQueryLog( + log: DatabaseQueryLogDomainModel, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ) { + databaseQueryLogDao.insert( + DatabaseQueryLogEntity( + dbName = log.dbName, + sqlQuery = log.sqlQuery, + bindArgs = log.bindArgs?.let { json.encodeToString(it) }, + timestamp = log.timestamp, + isTransaction = log.isTransaction, + deviceId = deviceIdAndPackageName.deviceId, + packageName = deviceIdAndPackageName.packageName, + appInstance = log.appInstance + ) + ) + } + + override fun observeQueryLogs( + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + dbName: String, + showTransactions: Boolean, + filters: List + ): Flow> { + return Pager( + config = PagingConfig( + pageSize = 20, + ), + pagingSourceFactory = { + val query = buildQuery( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = deviceIdAndPackageName + ) + + databaseQueryLogDao.getPagingSource( + query = query, + ) + } + ).flow + .map { pagingData -> + pagingData.map { entity -> + DatabaseQueryLogDomainModel( + dbName = entity.dbName, + sqlQuery = entity.sqlQuery, + bindArgs = entity.bindArgs?.let { + json.decodeFromString(it) + } ?: emptyList(), + timestamp = entity.timestamp, + isTransaction = entity.isTransaction, + appInstance = entity.appInstance + ) + } + } + } + + override suspend fun getQueryLogs( + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + dbName: String, + showTransactions: Boolean, + filters: List + ): List { + val query = buildQuery( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = deviceIdAndPackageName + ) + return databaseQueryLogDao.getLogs(query).map { entity -> + DatabaseQueryLogDomainModel( + dbName = entity.dbName, + sqlQuery = entity.sqlQuery, + bindArgs = entity.bindArgs?.let { + json.decodeFromString(it) + } ?: emptyList(), + timestamp = entity.timestamp, + isTransaction = entity.isTransaction, + appInstance = entity.appInstance + ) + } + } + + private fun buildQuery( + dbName: String, + showTransactions: Boolean, + filters: List, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ): RoomRawQuery { + val queryParams = ArrayList() + var queryString = "SELECT * FROM DatabaseQueryLogEntity WHERE deviceId = ? AND packageName = ? AND dbName = ?" + queryParams.add(deviceIdAndPackageName.deviceId) + queryParams.add(deviceIdAndPackageName.packageName) + queryParams.add(dbName) + + if (!showTransactions) { + queryString += " AND isTransaction = 0" + } + + val includes = filters.filter { it.type == FilterQueryLogDomainModel.FilterType.INCLUDE } + val excludes = filters.filter { it.type == FilterQueryLogDomainModel.FilterType.EXCLUDE } + + if (includes.isNotEmpty()) { + queryString += " AND (" + includes.forEachIndexed { index, filter -> + if (index > 0) queryString += " OR " + queryString += "(sqlQuery LIKE ? OR bindArgs LIKE ?)" + queryParams.add("%${filter.text}%") + queryParams.add("%${filter.text}%") + } + queryString += ")" + } + + if (excludes.isNotEmpty()) { + excludes.forEach { filter -> + queryString += " AND NOT (sqlQuery LIKE ? OR bindArgs LIKE ?)" + queryParams.add("%${filter.text}%") + queryParams.add("%${filter.text}%") + } + } + + queryString += " ORDER BY timestamp DESC" + + val query = RoomRawQuery( + sql = queryString, + onBindStatement = { statement -> + queryParams.forEachIndexed { index, arg -> + when (arg) { + is String -> statement.bindText(index + 1, arg) + is Long -> statement.bindLong(index + 1, arg) + is Int -> statement.bindLong(index + 1, arg.toLong()) + is Boolean -> statement.bindLong(index + 1, if (arg) 1L else 0L) + is Double -> statement.bindDouble(index + 1, arg) + is Float -> statement.bindDouble(index + 1, arg.toDouble()) + else -> statement.bindText(index + 1, arg.toString()) + } + } + } + ) + return query + } } diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt new file mode 100644 index 00000000..2ecbd53c --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/database/models/DatabaseQueryLogEntity.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.data.local.database.models + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class DatabaseQueryLogEntity( + @PrimaryKey(autoGenerate = true) val id: Int = 0, + val dbName: String, + val sqlQuery: String, + val bindArgs: String?, + val timestamp: Long, + val isTransaction: Boolean, + + val deviceId: String, + val packageName: String, + val appInstance: Long, +) diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt index aec55dde..83a4328b 100644 --- a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/datasource/QueryDatabaseRemoteDataSourceImpl.kt @@ -5,6 +5,7 @@ import com.flocon.data.remote.database.mapper.decodeDeviceDatabases import com.flocon.data.remote.database.mapper.decodeReceivedQuery import com.flocon.data.remote.database.models.DatabaseExecuteSqlResponseDataModel import com.flocon.data.remote.database.models.DatabaseOutgoingQueryMessage +import com.flocon.data.remote.database.models.DatabaseQueryLogModel import com.flocon.data.remote.database.models.ResponseAndRequestIdDataModel import com.flocon.data.remote.database.models.toDeviceDatabasesDomain import com.flocon.data.remote.models.FloconOutgoingMessageDataModel @@ -17,6 +18,7 @@ import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.common.Failure import io.github.openflocon.domain.common.Success import io.github.openflocon.domain.database.models.DatabaseExecuteSqlResponseDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId import io.github.openflocon.domain.database.models.ResponseAndRequestIdDomainModel @@ -84,6 +86,30 @@ class QueryDatabaseRemoteDataSourceImpl( override fun getDeviceDatabases(message: FloconIncomingMessageDomainModel): List = toDeviceDatabasesDomain(json.decodeDeviceDatabases(message.body).orEmpty()) override fun getReceiveQuery(message: FloconIncomingMessageDomainModel): ResponseAndRequestIdDomainModel? = json.decodeReceivedQuery(message.body)?.toDomain() + + override fun getQueryLogs( + message: FloconIncomingMessageDomainModel, + ): DatabaseQueryLogDomainModel? { + return json.decodeFromString(message.body)?.let { + + val upperQuery = it.sqlQuery.trim().uppercase() + val isTransaction = upperQuery == "BEGIN TRANSACTION" || + upperQuery == "COMMIT" || + upperQuery == "ROLLBACK" || + upperQuery == "END TRANSACTION" || + upperQuery == "TRANSACTION SUCCESSFUL" || + upperQuery == "BEGIN IMMEDIATE TRANSACTION" + + DatabaseQueryLogDomainModel( + dbName = it.dbName, + sqlQuery = it.sqlQuery, + bindArgs = it.bindArgs.orEmpty(), + timestamp = it.timestamp, + isTransaction = isTransaction, + appInstance = message.appInstance, + ) + } + } } // TODO internal diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt new file mode 100644 index 00000000..bb9a9790 --- /dev/null +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/database/models/DatabaseQueryLogModel.kt @@ -0,0 +1,11 @@ +package com.flocon.data.remote.database.models + +import kotlinx.serialization.Serializable + +@Serializable +data class DatabaseQueryLogModel( + val dbName: String, + val sqlQuery: String, + val bindArgs: List?, + val timestamp: Long, +) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt index 34fc05cb..d7c54ecc 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt @@ -35,6 +35,7 @@ object Protocol { object Method { const val Query = "query" const val GetDatabases = "getDatabases" + const val LogQuery = "logQuery" } } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/DI.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/DI.kt index e7b88d3b..80bc6a63 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/DI.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/DI.kt @@ -3,6 +3,7 @@ package io.github.openflocon.domain.database import io.github.openflocon.domain.database.usecase.AskForDeviceDatabasesUseCase import io.github.openflocon.domain.database.usecase.ExecuteDatabaseQueryUseCase import io.github.openflocon.domain.database.usecase.GetDatabaseByIdUseCase +import io.github.openflocon.domain.database.usecase.ObserveDatabaseQueryLogsUseCase import io.github.openflocon.domain.database.usecase.GetDeviceDatabaseTablesUseCase import io.github.openflocon.domain.database.usecase.GetTableColumnsUseCase import io.github.openflocon.domain.database.usecase.ObserveCurrentDeviceSelectedDatabaseAndTablesUseCase @@ -28,4 +29,5 @@ internal val databaseModule = module { factoryOf(::ObserveFavoriteQueriesUseCase) factoryOf(::SaveQueryAsFavoriteDatabaseUseCase) factoryOf(::GetFavoriteQueryByIdDatabaseUseCase) + factoryOf(::ObserveDatabaseQueryLogsUseCase) } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt new file mode 100644 index 00000000..228cd9c9 --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/DatabaseQueryLogDomainModel.kt @@ -0,0 +1,16 @@ +package io.github.openflocon.domain.database.models + +import io.github.openflocon.domain.database.utils.injectSqlArgs + +data class DatabaseQueryLogDomainModel( + val dbName: String, + val sqlQuery: String, + val bindArgs: List?, + val timestamp: Long, + val isTransaction: Boolean, + val appInstance: Long, +) + +fun DatabaseQueryLogDomainModel.toFullSql(): String { + return injectSqlArgs(sqlQuery, bindArgs) +} \ No newline at end of file diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/FilterQueryLogDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/FilterQueryLogDomainModel.kt new file mode 100644 index 00000000..36bf1c74 --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/models/FilterQueryLogDomainModel.kt @@ -0,0 +1,11 @@ +package io.github.openflocon.domain.database.models + +data class FilterQueryLogDomainModel( + val text: String, + val type: FilterType, +) { + enum class FilterType { + INCLUDE, + EXCLUDE + } +} \ No newline at end of file diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt index a601e4c6..d1bb1b4f 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/repository/DatabaseRepository.kt @@ -1,11 +1,14 @@ package io.github.openflocon.domain.database.repository +import androidx.paging.PagingData import io.github.openflocon.domain.common.Either import io.github.openflocon.domain.database.models.DatabaseExecuteSqlResponseDomainModel import io.github.openflocon.domain.database.models.DatabaseFavoriteQueryDomainModel +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel import io.github.openflocon.domain.database.models.DatabaseTableDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseDomainModel import io.github.openflocon.domain.database.models.DeviceDataBaseId +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import kotlinx.coroutines.flow.Flow @@ -72,6 +75,20 @@ interface DatabaseRepository { suspend fun getFavorite( deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, databaseId: String, - id: Long + id: Long, ): DatabaseFavoriteQueryDomainModel? + + fun observeQueryLogs( + dbName: String, + showTransactions: Boolean, + filters: List, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ): Flow> + + suspend fun getQueryLogs( + dbName: String, + showTransactions: Boolean, + filters: List, + deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel, + ): List } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt new file mode 100644 index 00000000..6fc592cf --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/GetDatabaseQueryLogsUseCase.kt @@ -0,0 +1,26 @@ +package io.github.openflocon.domain.database.usecase + +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel +import io.github.openflocon.domain.database.repository.DatabaseRepository +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase +import kotlinx.coroutines.flow.first + +class GetDatabaseQueryLogsUseCase( + private val databaseRepository: DatabaseRepository, + private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, +) { + suspend operator fun invoke( + dbName: String, + showTransactions: Boolean, + filters: List + ): List { + val current = observeCurrentDeviceIdAndPackageNameUseCase().first() ?: return emptyList() + return databaseRepository.getQueryLogs( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = current, + ) + } +} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt new file mode 100644 index 00000000..9e871391 --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/ObserveDatabaseQueryLogsUseCase.kt @@ -0,0 +1,34 @@ +package io.github.openflocon.domain.database.usecase + +import androidx.paging.PagingData +import io.github.openflocon.domain.database.models.DatabaseQueryLogDomainModel +import io.github.openflocon.domain.database.models.FilterQueryLogDomainModel +import io.github.openflocon.domain.database.repository.DatabaseRepository +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf + +class ObserveDatabaseQueryLogsUseCase( + private val databaseRepository: DatabaseRepository, + private val observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, +) { + operator fun invoke( + dbName: String, + showTransactions: Boolean, + filters: List + ): Flow> { + return observeCurrentDeviceIdAndPackageNameUseCase().flatMapLatest { current -> + if (current == null) { + return@flatMapLatest flowOf(PagingData.empty()) + } else { + databaseRepository.observeQueryLogs( + dbName = dbName, + showTransactions = showTransactions, + filters = filters, + deviceIdAndPackageName = current, + ) + } + } + } +} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/utils/SqlUtils.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/utils/SqlUtils.kt new file mode 100644 index 00000000..f984c635 --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/utils/SqlUtils.kt @@ -0,0 +1,13 @@ +package io.github.openflocon.domain.database.utils + +fun injectSqlArgs(sql: String, args: List?): String { + return if (args.isNullOrEmpty()) { + sql + } else { + var result = sql + args.forEach { arg -> + result = result.replaceFirst("?", "'$arg'") + } + result + } +} diff --git a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/common/FloconContextMenuItem.kt b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/common/FloconContextMenuItem.kt index 35880318..19a3da45 100644 --- a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/common/FloconContextMenuItem.kt +++ b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/common/FloconContextMenuItem.kt @@ -1,20 +1,25 @@ package io.github.openflocon.library.designsystem.common import androidx.compose.foundation.ContextMenuItem +import androidx.compose.runtime.Immutable +@Immutable sealed class FloconContextMenuItem( label: String, onClick: () -> Unit ) : ContextMenuItem(label = label, onClick = onClick) { // TODO Add icon + @Immutable class Item(label: String, onClick: () -> Unit) : FloconContextMenuItem(label, onClick) + @Immutable class SubMenu( label: String, val items: List ) : FloconContextMenuItem(label, onClick = {}) + @Immutable class Separator : FloconContextMenuItem(label = "", onClick = {}) }