Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

Contact Journal Event/Risk extension (EXPOSUREAPP-5413) #2696

Merged
merged 42 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d37456e
string preparation
BMItr Mar 15, 2021
ba7846b
Use modular recycler view for dairy days, adapter, items, layouts
BMItr Mar 16, 2021
8557e96
Use modular recycler view for dairy days - next
BMItr Mar 17, 2021
0e35f84
improved item handling
BMItr Mar 19, 2021
4d636ce
Added dividers, diff util
BMItr Mar 19, 2021
aa2511f
adjusted header logic
BMItr Mar 22, 2021
9ed86cc
Added final risk event for diary, refactored ENFitem and ENFvh, cleanup
BMItr Mar 23, 2021
f395e60
final logic for risk event diary
BMItr Mar 24, 2021
a3fd243
Adjusted baselne - diary extension
BMItr Mar 25, 2021
af52899
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 25, 2021
802eb7d
diary db migration and new attributes for event extension
BMItr Mar 25, 2021
3edc16d
cleanup, sourceCheck
BMItr Mar 26, 2021
6043ed8
clean code
BMItr Mar 26, 2021
a4ba0e8
Added migration tests, updated ContactDiaryDatabaseTest to utilize ne…
BMItr Mar 29, 2021
4704530
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 29, 2021
cae96b2
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 29, 2021
b720816
Replaced recyclerview with Flow, some layout improvements
BMItr Mar 29, 2021
564bd90
Removed unused viewholders
BMItr Mar 30, 2021
0a160af
Fixed contact diary test
BMItr Mar 30, 2021
94a712c
Removed debug code
BMItr Mar 30, 2021
44a2a5e
Fixed broken tests
BMItr Mar 30, 2021
aa03466
Added test for risk by event
BMItr Mar 30, 2021
3e39d22
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 30, 2021
0c5648a
Adjusted spacing
BMItr Mar 30, 2021
fe8c62f
cleanup, missing itemDecoration
BMItr Mar 30, 2021
fad79a8
cleanup, detekt, reduced complexity
BMItr Mar 30, 2021
931fb35
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
harambasicluka Mar 30, 2021
0b626c2
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
harambasicluka Mar 31, 2021
c3a79f7
named params
BMItr Mar 31, 2021
41011b4
Use srcCompat
BMItr Mar 31, 2021
bc3e4c9
replaced event with trace location
BMItr Mar 31, 2021
7910de4
Respecting the tech spec
BMItr Mar 31, 2021
87de9c7
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 31, 2021
9df09cf
conflicts resolved for OverviewViewModel
BMItr Mar 31, 2021
6826a75
source clean
BMItr Mar 31, 2021
1e7161d
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 31, 2021
4fca5cc
fix conflicts to newest 2.0 merge
BMItr Mar 31, 2021
2443a06
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 31, 2021
91602de
resolve merged migration test conflict
BMItr Mar 31, 2021
0d56f38
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 31, 2021
c5491b7
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
BMItr Mar 31, 2021
f856524
Merge branch 'release/2.0.x' into feature/5413-Contact-Journal-Event-…
mtwalli Mar 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "f962ae2729d9b71eb3ed3fc51a110b95",
"entities": [
{
"tableName": "locations",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationName` TEXT NOT NULL, `phoneNumber` TEXT, `emailAddress` TEXT, `traceLocationGUID` TEXT)",
"fields": [
{
"fieldPath": "locationId",
"columnName": "locationId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "locationName",
"columnName": "locationName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "phoneNumber",
"columnName": "phoneNumber",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "emailAddress",
"columnName": "emailAddress",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "traceLocationGUID",
"columnName": "traceLocationGUID",
BMItr marked this conversation as resolved.
Show resolved Hide resolved
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"locationId"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "locationvisits",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkLocationId` INTEGER NOT NULL, `duration` INTEGER, `circumstances` TEXT, `checkInID` INTEGER, FOREIGN KEY(`fkLocationId`) REFERENCES `locations`(`locationId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fkLocationId",
"columnName": "fkLocationId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "duration",
"columnName": "duration",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "circumstances",
"columnName": "circumstances",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "checkInID",
"columnName": "checkInID",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_locationvisits_fkLocationId",
"unique": false,
"columnNames": [
"fkLocationId"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_locationvisits_fkLocationId` ON `${TABLE_NAME}` (`fkLocationId`)"
}
],
"foreignKeys": [
{
"table": "locations",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"fkLocationId"
],
"referencedColumns": [
"locationId"
]
}
]
},
{
"tableName": "persons",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`personId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fullName` TEXT NOT NULL, `phoneNumber` TEXT, `emailAddress` TEXT)",
"fields": [
{
"fieldPath": "personId",
"columnName": "personId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "fullName",
"columnName": "fullName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "phoneNumber",
"columnName": "phoneNumber",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "emailAddress",
"columnName": "emailAddress",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"personId"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "personencounters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkPersonId` INTEGER NOT NULL, `durationClassification` TEXT, `withMask` INTEGER, `wasOutside` INTEGER, `circumstances` TEXT, FOREIGN KEY(`fkPersonId`) REFERENCES `persons`(`personId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "fkPersonId",
"columnName": "fkPersonId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "durationClassification",
"columnName": "durationClassification",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "withMask",
"columnName": "withMask",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "wasOutside",
"columnName": "wasOutside",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "circumstances",
"columnName": "circumstances",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_personencounters_fkPersonId",
"unique": false,
"columnNames": [
"fkPersonId"
],
"createSql": "CREATE INDEX IF NOT EXISTS `index_personencounters_fkPersonId` ON `${TABLE_NAME}` (`fkPersonId`)"
}
],
"foreignKeys": [
{
"table": "persons",
"onDelete": "CASCADE",
"onUpdate": "CASCADE",
"columns": [
"fkPersonId"
],
"referencedColumns": [
"personId"
]
}
]
}
],
"views": [],
"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, 'f962ae2729d9b71eb3ed3fc51a110b95')"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.rki.coronawarnapp.contactdiary.storage

import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteException
import androidx.room.testing.MigrationTestHelper
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
Expand All @@ -13,10 +15,12 @@ import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEncoun
import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEncounterWrapper
import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
import de.rki.coronawarnapp.contactdiary.storage.internal.migrations.ContactDiaryDatabaseMigration1To2
import de.rki.coronawarnapp.contactdiary.storage.internal.migrations.ContactDiaryDatabaseMigration2To3
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.joda.time.Duration
import org.joda.time.LocalDate
import org.junit.Rule
import org.junit.Test
Expand Down Expand Up @@ -110,7 +114,8 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() {
locationId = 1,
locationName = "Location1",
phoneNumber = null,
emailAddress = null
emailAddress = null,
null
BMItr marked this conversation as resolved.
Show resolved Hide resolved
)
runBlocking { daoDb.locationDao().allEntries().first() }.single() shouldBe location

Expand All @@ -131,7 +136,8 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() {
date = LocalDate.parse("2020-04-20"),
fkLocationId = 1,
duration = null,
circumstances = null
circumstances = null,
null
BMItr marked this conversation as resolved.
Show resolved Hide resolved
)
)

Expand All @@ -151,6 +157,79 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() {
)
}

@Test
fun migrate2To3() {
val location = ContactDiaryLocationEntity(
locationId = 1,
locationName = "Why do you want to have this information?",
phoneNumber = "I'm not going to tell you yet",
emailAddress = "I'mnotgoingtotell@you.yet",
traceLocationGUID = null
)

val locationVisit = ContactDiaryLocationVisitEntity(
id = 2,
date = LocalDate.parse("2020-12-31"),
fkLocationId = 1,
duration = Duration.standardMinutes(13),
circumstances = "That's none of your business",
null
BMItr marked this conversation as resolved.
Show resolved Hide resolved
)

val locationAfter = location.copy(traceLocationGUID = "jshrgu-aifhioaio-aofsjof-samofp-kjsadngsgf")
val locationVisitAfter = locationVisit.copy(checkInID = 101)

val locationValues = ContentValues().apply {
put("locationId", location.locationId)
put("locationName", location.locationName)
put("phoneNumber", location.phoneNumber)
put("emailAddress", location.emailAddress)
}

val locationVisitValues = ContentValues().apply {
put("id", locationVisit.id)
put("date", locationVisit.date.toString())
put("fkLocationId", locationVisit.fkLocationId)
put("duration", locationVisit.duration?.millis)
put("circumstances", locationVisit.circumstances)
}

helper.createDatabase(DB_NAME, 2).apply {
insert("locations", SQLiteDatabase.CONFLICT_FAIL, locationValues)
insert("locationvisits", SQLiteDatabase.CONFLICT_FAIL, locationVisitValues)

close()
}

// Run migration
helper.runMigrationsAndValidate(
DB_NAME,
3,
true,
ContactDiaryDatabaseMigration2To3
)

val daoDb = ContactDiaryDatabase.Factory(
ctx = ApplicationProvider.getApplicationContext()
).create(databaseName = DB_NAME)

runBlocking {
daoDb.locationVisitDao().allEntries().first().single() shouldBe ContactDiaryLocationVisitWrapper(
contactDiaryLocationEntity = location,
contactDiaryLocationVisitEntity = locationVisit
)

// Test if new attributes are added correctly
daoDb.locationDao().update(locationAfter)
daoDb.locationVisitDao().update(locationVisitAfter)

daoDb.locationVisitDao().allEntries().first().single() shouldBe ContactDiaryLocationVisitWrapper(
contactDiaryLocationEntity = locationAfter,
contactDiaryLocationVisitEntity = locationVisitAfter
)
}
}

@Test
fun migrate1To2_failure_throws() {
helper.createDatabase(DB_NAME, 1).apply {
Expand All @@ -177,6 +256,26 @@ class ContactDiaryDatabaseMigrationTest : BaseTestInstrumentation() {
}
}

@Test
fun migrate2To3_failure_throws() {
helper.createDatabase(DB_NAME, 2).apply {
execSQL("DROP TABLE IF EXISTS locations")
// Has incompatible existing column phoneNumber of wrong type
execSQL("CREATE TABLE IF NOT EXISTS `locations` (`locationId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationName` TEXT NOT NULL, `phoneNumber` TEXT, `emailAddress` TEXT, `traceLocationGUID` INTEGER)")
close()
}

shouldThrow<SQLiteException> {
// Run migration
helper.runMigrationsAndValidate(
DB_NAME,
3,
true,
ContactDiaryDatabaseMigration2To3
)
}
}

@Test
@Throws(IOException::class)
fun migrateAll() {
Expand Down
Loading