Files
box-manifest/android/src/main/java/app/boxmanifest/data/LocalInventoryDatabase.kt
T

128 lines
3.9 KiB
Kotlin

package app.boxmanifest.data
import android.content.Context
import androidx.room.Dao
import androidx.room.Database
import androidx.room.Entity
import androidx.room.Index
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.PrimaryKey
import androidx.room.Query
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.Transaction
@Entity(tableName = "nodes", indices = [Index("parentId")])
data class LocalNode(
@PrimaryKey val id: String,
val lookupCode: String,
val parentId: String?,
val name: String,
val description: String?,
val quantity: Long?,
val typeId: String?,
val tagIdsJson: String,
val revision: Long,
val sortOrder: Int,
val pendingCreate: Boolean = false,
)
@Entity(tableName = "pending_operations", indices = [Index(value = ["nodeId"], unique = true)])
data class PendingOperation(
@PrimaryKey(autoGenerate = true) val operationId: Long = 0,
val kind: String,
val nodeId: String,
val parentId: String?,
val name: String,
val description: String?,
val quantity: Long?,
val baseRevision: Long?,
val status: String = STATUS_PENDING,
val error: String? = null,
) {
companion object {
const val CREATE = "CREATE"
const val UPDATE = "UPDATE"
const val STATUS_PENDING = "PENDING"
const val STATUS_CONFLICT = "CONFLICT"
}
}
@Dao
interface InventoryDao {
@Query("SELECT * FROM nodes ORDER BY sortOrder")
suspend fun nodes(): List<LocalNode>
@Query("SELECT * FROM nodes WHERE id = :id")
suspend fun node(id: String): LocalNode?
@Query("SELECT COALESCE(MAX(sortOrder), -1) + 1 FROM nodes")
suspend fun nextSortOrder(): Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun putNode(node: LocalNode)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun putNodes(nodes: List<LocalNode>)
@Query("DELETE FROM nodes")
suspend fun deleteNodes()
@Transaction
suspend fun replaceNodes(nodes: List<LocalNode>) {
deleteNodes()
putNodes(nodes)
}
@Query("SELECT * FROM pending_operations ORDER BY operationId")
suspend fun operations(): List<PendingOperation>
@Query("SELECT * FROM pending_operations WHERE nodeId = :nodeId")
suspend fun operationFor(nodeId: String): PendingOperation?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun putOperation(operation: PendingOperation)
@Query("DELETE FROM pending_operations WHERE operationId = :id")
suspend fun deleteOperation(id: Long)
@Query("UPDATE pending_operations SET status = :status, error = :error WHERE operationId = :id")
suspend fun markOperation(id: Long, status: String, error: String?)
@Query("DELETE FROM pending_operations")
suspend fun deleteOperations()
@Query("SELECT COUNT(*) FROM pending_operations WHERE status = 'PENDING'")
suspend fun pendingCount(): Int
@Query("SELECT error FROM pending_operations WHERE status = 'CONFLICT' ORDER BY operationId LIMIT 1")
suspend fun firstConflict(): String?
@Query("SELECT * FROM pending_operations WHERE status = 'CONFLICT' ORDER BY operationId LIMIT 1")
suspend fun firstConflictOperation(): PendingOperation?
@Transaction
suspend fun clearAll() {
deleteOperations()
deleteNodes()
}
}
@Database(entities = [LocalNode::class, PendingOperation::class], version = 1, exportSchema = false)
abstract class LocalInventoryDatabase : RoomDatabase() {
abstract fun inventory(): InventoryDao
companion object {
@Volatile private var instance: LocalInventoryDatabase? = null
fun get(context: Context): LocalInventoryDatabase = instance ?: synchronized(this) {
instance ?: Room.databaseBuilder(
context.applicationContext,
LocalInventoryDatabase::class.java,
"box-manifest-local.db",
).build().also { instance = it }
}
}
}