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 +}