Add new my story migration.
This commit is contained in:
parent
b002235ef7
commit
a0cc2ff90a
3 changed files with 256 additions and 1 deletions
|
@ -0,0 +1,119 @@
|
||||||
|
package org.thoughtcrime.securesms.database.helpers.migration
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.fail
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.signal.core.util.SqlUtil
|
||||||
|
import org.thoughtcrime.securesms.database.DistributionListDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||||
|
import org.thoughtcrime.securesms.testing.SignalDatabaseRule
|
||||||
|
import org.whispersystems.signalservice.api.push.DistributionId
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MyStoryMigrationTest {
|
||||||
|
|
||||||
|
@get:Rule val harness = SignalDatabaseRule(deleteAllThreadsOnEachRun = false)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenAValidMyStory_whenIMigrate_thenIExpectMyStoryToBeValid() {
|
||||||
|
// GIVEN
|
||||||
|
assertValidMyStoryExists()
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
runMigration()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
assertValidMyStoryExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenNoMyStory_whenIMigrate_thenIExpectMyStoryToBeCreated() {
|
||||||
|
// GIVEN
|
||||||
|
deleteMyStory()
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
runMigration()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
assertValidMyStoryExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenA00000000DistributionIdForMyStory_whenIMigrate_thenIExpectMyStoryToBeCreated() {
|
||||||
|
// GIVEN
|
||||||
|
setMyStoryDistributionId("0000-0000")
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
runMigration()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
assertValidMyStoryExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun givenARandomDistributionIdForMyStory_whenIMigrate_thenIExpectMyStoryToBeCreated() {
|
||||||
|
// GIVEN
|
||||||
|
setMyStoryDistributionId(UUID.randomUUID().toString())
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
runMigration()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
assertValidMyStoryExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setMyStoryDistributionId(serializedId: String) {
|
||||||
|
SignalDatabase.rawDatabase.update(
|
||||||
|
DistributionListDatabase.LIST_TABLE_NAME,
|
||||||
|
contentValuesOf(
|
||||||
|
DistributionListDatabase.DISTRIBUTION_ID to serializedId
|
||||||
|
),
|
||||||
|
"_id = ?",
|
||||||
|
SqlUtil.buildArgs(DistributionListId.MY_STORY)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteMyStory() {
|
||||||
|
SignalDatabase.rawDatabase.delete(
|
||||||
|
DistributionListDatabase.LIST_TABLE_NAME,
|
||||||
|
"_id = ?",
|
||||||
|
SqlUtil.buildArgs(DistributionListId.MY_STORY)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertValidMyStoryExists() {
|
||||||
|
SignalDatabase.rawDatabase.query(
|
||||||
|
DistributionListDatabase.LIST_TABLE_NAME,
|
||||||
|
SqlUtil.COUNT,
|
||||||
|
"_id = ? AND ${DistributionListDatabase.DISTRIBUTION_ID} = ?",
|
||||||
|
SqlUtil.buildArgs(DistributionListId.MY_STORY, DistributionId.MY_STORY.toString()),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).use {
|
||||||
|
if (it.moveToNext()) {
|
||||||
|
val count = it.getInt(0)
|
||||||
|
assertEquals("assertValidMyStoryExists: Query produced an unexpected count.", 1, count)
|
||||||
|
} else {
|
||||||
|
fail("assertValidMyStoryExists: Query did not produce a count.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun runMigration() {
|
||||||
|
MyStoryMigration.migrate(
|
||||||
|
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as Application,
|
||||||
|
SignalDatabase.rawDatabase,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||||
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper.entrySet
|
import org.thoughtcrime.securesms.conversation.colors.ChatColorsMapper.entrySet
|
||||||
import org.thoughtcrime.securesms.database.KeyValueDatabase
|
import org.thoughtcrime.securesms.database.KeyValueDatabase
|
||||||
import org.thoughtcrime.securesms.database.RecipientDatabase
|
import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.helpers.migration.MyStoryMigration
|
||||||
import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration
|
import org.thoughtcrime.securesms.database.helpers.migration.UrgentMslFlagMigration
|
||||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList
|
||||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
@ -205,8 +206,9 @@ object SignalDatabaseMigrations {
|
||||||
private const val MY_STORY_PRIVACY_MODE = 148
|
private const val MY_STORY_PRIVACY_MODE = 148
|
||||||
private const val EXPIRING_PROFILE_CREDENTIALS = 149
|
private const val EXPIRING_PROFILE_CREDENTIALS = 149
|
||||||
private const val URGENT_FLAG = 150
|
private const val URGENT_FLAG = 150
|
||||||
|
private const val MY_STORY_MIGRATION = 151
|
||||||
|
|
||||||
const val DATABASE_VERSION = 150
|
const val DATABASE_VERSION = 151
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
@ -2669,6 +2671,10 @@ object SignalDatabaseMigrations {
|
||||||
if (oldVersion < URGENT_FLAG) {
|
if (oldVersion < URGENT_FLAG) {
|
||||||
UrgentMslFlagMigration.migrate(context, db, oldVersion, newVersion)
|
UrgentMslFlagMigration.migrate(context, db, oldVersion, newVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < MY_STORY_MIGRATION) {
|
||||||
|
MyStoryMigration.migrate(context, db, oldVersion, newVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package org.thoughtcrime.securesms.database.helpers.migration
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.database.Cursor
|
||||||
|
import androidx.core.content.contentValuesOf
|
||||||
|
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||||
|
import org.signal.core.util.CursorUtil
|
||||||
|
import org.signal.core.util.SqlUtil
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||||
|
import org.thoughtcrime.securesms.util.Base64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a check and ensures that MyStory exists at the correct distribution list id and correct distribution id.
|
||||||
|
*/
|
||||||
|
object MyStoryMigration : SignalDatabaseMigration {
|
||||||
|
|
||||||
|
private val TAG = Log.tag(MyStoryMigration::class.java)
|
||||||
|
|
||||||
|
private const val TABLE_NAME = "distribution_list"
|
||||||
|
private const val NAME = "name"
|
||||||
|
private const val DISTRIBUTION_LIST_ID = "_id"
|
||||||
|
private const val DISTRIBUTION_ID = "distribution_id"
|
||||||
|
private const val RECIPIENT_ID = "recipient_id"
|
||||||
|
private const val PRIVACY_MODE = "privacy_mode"
|
||||||
|
private const val MY_STORY_DISTRIBUTION_LIST_ID = 1
|
||||||
|
private const val MY_STORY_DISTRIBUTION_ID = "00000000-0000-0000-0000-000000000000"
|
||||||
|
private const val MY_STORY_PRIVACY_MODE = 2
|
||||||
|
|
||||||
|
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
val result: MyStoryExistsResult = getMyStoryCursor(db).use { cursor ->
|
||||||
|
if (cursor.moveToNext()) {
|
||||||
|
val distributionId = CursorUtil.requireString(cursor, DISTRIBUTION_ID)
|
||||||
|
if (distributionId != MY_STORY_DISTRIBUTION_ID) {
|
||||||
|
Log.d(TAG, "[migrate] Invalid MyStory DistributionId: $distributionId")
|
||||||
|
MyStoryExistsResult.REQUIRES_DISTRIBUTION_ID_UPDATE
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "[migrate] MyStory DistributionId matches expected value.")
|
||||||
|
MyStoryExistsResult.MATCHES_EXPECTED_VALUE
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "[migrate] My Story does not exist.")
|
||||||
|
MyStoryExistsResult.DOES_NOT_EXIST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
MyStoryExistsResult.REQUIRES_DISTRIBUTION_ID_UPDATE -> updateDistributionIdToExpectedValue(db)
|
||||||
|
MyStoryExistsResult.MATCHES_EXPECTED_VALUE -> Unit
|
||||||
|
MyStoryExistsResult.DOES_NOT_EXIST -> createMyStory(db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateDistributionIdToExpectedValue(db: SQLiteDatabase) {
|
||||||
|
Log.d(TAG, "[updateDistributionIdToExpectedValue] Overwriting My Story DistributionId with expected value.")
|
||||||
|
db.update(
|
||||||
|
TABLE_NAME,
|
||||||
|
contentValuesOf(DISTRIBUTION_ID to MY_STORY_DISTRIBUTION_ID),
|
||||||
|
"$DISTRIBUTION_LIST_ID = ?",
|
||||||
|
arrayOf(MY_STORY_DISTRIBUTION_LIST_ID.toString())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMyStory(db: SQLiteDatabase) {
|
||||||
|
Log.d(TAG, "[createMyStory] Attempting to create My Story.")
|
||||||
|
|
||||||
|
val recipientId: Long = getMyStoryRecipientId(db) ?: createMyStoryRecipientId(db)
|
||||||
|
|
||||||
|
db.insert(
|
||||||
|
TABLE_NAME,
|
||||||
|
null,
|
||||||
|
contentValuesOf(
|
||||||
|
DISTRIBUTION_LIST_ID to MY_STORY_DISTRIBUTION_LIST_ID,
|
||||||
|
NAME to MY_STORY_DISTRIBUTION_ID,
|
||||||
|
DISTRIBUTION_ID to MY_STORY_DISTRIBUTION_ID,
|
||||||
|
RECIPIENT_ID to recipientId,
|
||||||
|
PRIVACY_MODE to MY_STORY_PRIVACY_MODE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMyStoryRecipientId(db: SQLiteDatabase): Long {
|
||||||
|
return db.insert(
|
||||||
|
"recipient",
|
||||||
|
null,
|
||||||
|
contentValuesOf(
|
||||||
|
"group_type" to 4,
|
||||||
|
"distribution_list_id" to MY_STORY_DISTRIBUTION_LIST_ID,
|
||||||
|
"storage_service_key" to Base64.encodeBytes(StorageSyncHelper.generateKey()),
|
||||||
|
"profile_sharing" to 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMyStoryRecipientId(db: SQLiteDatabase): Long? {
|
||||||
|
return db.query(
|
||||||
|
"recipient",
|
||||||
|
arrayOf("_id"),
|
||||||
|
"distribution_list_id = ?",
|
||||||
|
SqlUtil.buildArgs(MY_STORY_DISTRIBUTION_LIST_ID),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).use {
|
||||||
|
if (it.moveToNext()) {
|
||||||
|
CursorUtil.requireLong(it, "_id")
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMyStoryCursor(db: SQLiteDatabase): Cursor {
|
||||||
|
return db.query(
|
||||||
|
TABLE_NAME,
|
||||||
|
arrayOf(DISTRIBUTION_ID),
|
||||||
|
"$DISTRIBUTION_LIST_ID = ?",
|
||||||
|
arrayOf(MY_STORY_DISTRIBUTION_LIST_ID.toString()),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class MyStoryExistsResult {
|
||||||
|
REQUIRES_DISTRIBUTION_ID_UPDATE,
|
||||||
|
MATCHES_EXPECTED_VALUE,
|
||||||
|
DOES_NOT_EXIST
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue