feat(data): DAO Room FTS et CrmDatabase
EBO <eric.bouhana@softalys.com> committé le 2026-07-22 15:24
e2bf39c4a5b5e1f15a80dff6aae0979f04344300
1 parent(s)
3 fichiers modifiés
+121
-6
A
android/app/src/main/java/fr/ebii/card2vcf/data/CrmContactDao.kt
+46
-0
@@ -0,0 +1,46 @@
| 1 | +package fr.ebii.card2vcf.data | |
| 2 | + | |
| 3 | +import androidx.room.Dao | |
| 4 | +import androidx.room.Insert | |
| 5 | +import androidx.room.OnConflictStrategy | |
| 6 | +import androidx.room.Query | |
| 7 | +import androidx.room.Update | |
| 8 | +import kotlinx.coroutines.flow.Flow | |
| 9 | + | |
| 10 | +@Dao | |
| 11 | +interface CrmContactDao { | |
| 12 | + @Insert(onConflict = OnConflictStrategy.REPLACE) | |
| 13 | + suspend fun insert(entity: CrmContactEntity): Long | |
| 14 | + | |
| 15 | + @Update | |
| 16 | + suspend fun update(entity: CrmContactEntity) | |
| 17 | + | |
| 18 | + @Query("DELETE FROM crm_contacts WHERE id = :id") | |
| 19 | + suspend fun deleteById(id: Long) | |
| 20 | + | |
| 21 | + @Query("SELECT * FROM crm_contacts WHERE id = :id") | |
| 22 | + suspend fun getById(id: Long): CrmContactEntity? | |
| 23 | + | |
| 24 | + @Query("SELECT * FROM crm_contacts ORDER BY createdAt DESC") | |
| 25 | + fun observeAll(): Flow<List<CrmContactEntity>> | |
| 26 | + | |
| 27 | + @Query("SELECT * FROM crm_contacts ORDER BY createdAt DESC") | |
| 28 | + suspend fun getAll(): List<CrmContactEntity> | |
| 29 | + | |
| 30 | + @Query( | |
| 31 | + """ | |
| 32 | + SELECT rowid FROM crm_contacts_fts | |
| 33 | + WHERE crm_contacts_fts MATCH :query | |
| 34 | + """, | |
| 35 | + ) | |
| 36 | + suspend fun searchIds(query: String): List<Long> | |
| 37 | + | |
| 38 | + @Insert(onConflict = OnConflictStrategy.IGNORE) | |
| 39 | + suspend fun insertExclusion(ex: DuplicateExclusionEntity): Long | |
| 40 | + | |
| 41 | + @Query("SELECT * FROM duplicate_exclusions") | |
| 42 | + suspend fun getExclusions(): List<DuplicateExclusionEntity> | |
| 43 | + | |
| 44 | + @Query("DELETE FROM duplicate_exclusions WHERE idA = :id OR idB = :id") | |
| 45 | + suspend fun deleteExclusionsFor(id: Long) | |
| 46 | +} |
M
android/app/src/main/java/fr/ebii/card2vcf/data/CrmDatabase.kt
+27
-6
@@ -1,13 +1,34 @@
| 1 | 1 | package fr.ebii.card2vcf.data |
| 2 | 2 | |
| 3 | 3 | import android.content.Context |
| 4 | +import androidx.room.Database | |
| 5 | +import androidx.room.Room | |
| 6 | +import androidx.room.RoomDatabase | |
| 7 | +import androidx.room.TypeConverters | |
| 8 | + | |
| 9 | +@Database( | |
| 10 | + entities = [ | |
| 11 | + CrmContactEntity::class, | |
| 12 | + CrmContactFts::class, | |
| 13 | + DuplicateExclusionEntity::class, | |
| 14 | + ], | |
| 15 | + version = 1, | |
| 16 | + exportSchema = false, | |
| 17 | +) | |
| 18 | +@TypeConverters(Converters::class) | |
| 19 | +abstract class CrmDatabase : RoomDatabase() { | |
| 20 | + abstract fun crmContactDao(): CrmContactDao | |
| 4 | 21 | |
| 5 | -/** | |
| 6 | - * Stub temporaire pour compiler Card2vcfApp tant que Task 2 n'a pas fourni | |
| 7 | - * la vraie base Room (DAO + @Database). | |
| 8 | - */ | |
| 9 | -class CrmDatabase private constructor() { | |
| 10 | 22 | companion object { |
| 11 | - fun get(context: Context): CrmDatabase = CrmDatabase() | |
| 23 | + @Volatile private var instance: CrmDatabase? = null | |
| 24 | + | |
| 25 | + fun get(context: Context): CrmDatabase = | |
| 26 | + instance ?: synchronized(this) { | |
| 27 | + instance ?: Room.databaseBuilder( | |
| 28 | + context.applicationContext, | |
| 29 | + CrmDatabase::class.java, | |
| 30 | + "card2vcf-crm.db", | |
| 31 | + ).build().also { instance = it } | |
| 32 | + } | |
| 12 | 33 | } |
| 13 | 34 | } |
A
android/app/src/test/java/fr/ebii/card2vcf/data/CrmContactDaoTest.kt
+48
-0
@@ -0,0 +1,48 @@
| 1 | +package fr.ebii.card2vcf.data | |
| 2 | + | |
| 3 | +import android.content.Context | |
| 4 | +import androidx.room.Room | |
| 5 | +import androidx.test.core.app.ApplicationProvider | |
| 6 | +import kotlinx.coroutines.runBlocking | |
| 7 | +import org.junit.After | |
| 8 | +import org.junit.Assert.assertEquals | |
| 9 | +import org.junit.Before | |
| 10 | +import org.junit.Test | |
| 11 | +import org.junit.runner.RunWith | |
| 12 | +import org.robolectric.RobolectricTestRunner | |
| 13 | +import org.robolectric.annotation.Config | |
| 14 | + | |
| 15 | +@RunWith(RobolectricTestRunner::class) | |
| 16 | +@Config(sdk = [31]) | |
| 17 | +class CrmContactDaoTest { | |
| 18 | + private lateinit var db: CrmDatabase | |
| 19 | + private lateinit var dao: CrmContactDao | |
| 20 | + | |
| 21 | + @Before | |
| 22 | + fun setUp() { | |
| 23 | + val ctx = ApplicationProvider.getApplicationContext<Context>() | |
| 24 | + db = Room.inMemoryDatabaseBuilder(ctx, CrmDatabase::class.java) | |
| 25 | + .allowMainThreadQueries() | |
| 26 | + .build() | |
| 27 | + dao = db.crmContactDao() | |
| 28 | + } | |
| 29 | + | |
| 30 | + @After | |
| 31 | + fun tearDown() = db.close() | |
| 32 | + | |
| 33 | + @Test | |
| 34 | + fun insertAndSearchNotes() = runBlocking { | |
| 35 | + val id = dao.insert( | |
| 36 | + CrmContactEntity( | |
| 37 | + fullName = "Ada", | |
| 38 | + notes = "salon Lyon 2026", | |
| 39 | + phonesText = "", | |
| 40 | + emailsText = "", | |
| 41 | + createdAt = 1L, | |
| 42 | + updatedAt = 1L, | |
| 43 | + ), | |
| 44 | + ) | |
| 45 | + val hits = dao.searchIds("Lyon*") | |
| 46 | + assertEquals(listOf(id), hits) | |
| 47 | + } | |
| 48 | +} |
GitRust