Commit 7ffac0ae authored by Christophe Henry's avatar Christophe Henry

Merge branch 'migrate-account' into 'develop'

Fix crash happening when account's server instance URL has no protocol

See merge request !25
parents cca38bed 69554753
[*.{kt,kts}]
itrim_trailing_whitespace=true
insert_final_newline=true
max_line_length=120
\ No newline at end of file
......@@ -89,7 +89,7 @@ spotless {
dependencies {
def lifecycle_version = "2.0.0"
def room_version = "2.1.0-alpha04"
def roomigrant_version = "0.1.1"
def roomigrant_version = "0.1.7"
def fuel_version = "2.0.1"
def jackson_version = "2.9.6"
def espresso_version = "3.1.1"
......
INSERT INTO accounts VALUES (1, "User/e3b57290-6bf9-4fab-a2d7-11a35f2301e2", "User/e3b57290-6bf9-4fab-a2d7-11a35f2301e2", "User", "rss.user.tld");
INSERT INTO accounts VALUES (2, "User2/d016dbc2-4e5b-48d6-8c24-8e2523e62bd2", "User2/d016dbc2-4e5b-48d6-8c24-8e2523e62bd2", "User2", "http://rss.user.tld");
INSERT INTO accounts VALUES (3, "User3/0053ff77-4d92-45a3-ac50-43ae829a2a9f", "User3/0053ff77-4d92-45a3-ac50-43ae829a2a9f", "User3", "https://rss.user.tld/api/greader.php");
INSERT INTO accounts VALUES (4, "User4/0053ff77-4d92-45a3-ac50-43ae829a2a9f", "User4/0053ff77-4d92-45a3-ac50-43ae829a2a9f", "User4", "rss.user.tld/p/api/greader.php");
......@@ -47,7 +47,7 @@ abstract class FreshRSSDabatabaseBaseTest {
fun migrateDatabase(database: SupportSQLiteDatabase, from: Int, to: Int) {
val migrations = FreshRSSDabatabase_Migrations.build()
(from..(to - 1)).forEach {version ->
(from until to).forEach {version ->
val migration = migrations[version - 1]
val nextPreMigrationScript = "migration-test-${migration.endVersion}-before.sql"
val nextPostMigrationScript = "migration-test-${migration.endVersion}-after.sql"
......
package fr.chenry.android.freshrss.store.database
import androidx.test.runner.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
class FreshRSSDabatabaseMigrationsTest: FreshRSSDabatabaseBaseTest() {
@RunWith(AndroidJUnit4::class)
class FreshRSSDabatabaseMigrationsTest : FreshRSSDabatabaseBaseTest() {
@Test
fun allMigrations() {
createDatabaseFromScratch(FreshRSSDabatabase_Migrations.build().size)
......@@ -56,4 +59,36 @@ class FreshRSSDabatabaseMigrationsTest: FreshRSSDabatabaseBaseTest() {
database.close()
}
@Test
fun testMigrate6to7() {
createDatabaseFromScratch(6)
val migration = FreshRSSDabatabase_Migrations.build()[5]
var query = database.rawQuery("SELECT * FROM accounts", arrayOf())
assertEquals(4, query.count)
query.moveToFirst()
assertEquals("rss.user.tld", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("http://rss.user.tld", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("https://rss.user.tld/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("rss.user.tld/p/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
helper.runMigrationsAndValidate(testDB, migration.endVersion, true, migration)
query = database.rawQuery("SELECT * FROM accounts", arrayOf())
assertEquals(4, query.count)
query.moveToFirst()
assertEquals("https://rss.user.tld/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("http://rss.user.tld/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("https://rss.user.tld/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
query.moveToNext()
assertEquals("https://rss.user.tld/p/api/greader.php", query.getString(query.getColumnIndex("serverInstance")))
}
}
......@@ -11,9 +11,9 @@ import nl.komponents.kovenant.*
import nl.komponents.kovenant.functional.bind
import org.joda.time.LocalDateTime
@Database(version = 6, entities = [Account::class, Article::class, Subscription::class, SubscriptionCategory::class])
@Database(version = 7, entities = [Account::class, Article::class, Subscription::class, SubscriptionCategory::class])
@TypeConverters(Converters::class)
@GenerateRoomMigrations
@GenerateRoomMigrations(ManualMigrations::class)
abstract class FreshRSSDabatabase: RoomDatabase() {
protected abstract fun getAuthTokensDAO(): AuthTokensDAO
protected abstract fun getArticlesDAO(): ArticlesDAO
......
package fr.chenry.android.freshrss.store.database
import androidx.sqlite.db.SupportSQLiteDatabase
import dev.matrix.roomigrant.rules.OnMigrationStartRule
import fr.chenry.android.freshrss.utils.InstanceUrl
class ManualMigrations {
@OnMigrationStartRule(version1 = 6, version2 = 7)
fun migrate6To7before(
db: SupportSQLiteDatabase,
@Suppress("UNUSED_PARAMETER") version1: Int,
@Suppress("UNUSED_PARAMETER") version2: Int
) {
val cursor = db.query("SELECT DISTINCT serverInstance FROM accounts")
if (cursor.count == 0) return
cursor.moveToFirst()
do {
val serverInstance = cursor.getString(0)
val newServerInstance = serverInstance
.let { if (!it.contains("://")) "https://$it" else it }
.let { if (!it.endsWith(InstanceUrl.apiEndpoint)) "$it${InstanceUrl.apiEndpoint}" else it }
db.execSQL(
"UPDATE accounts SET serverInstance = '$newServerInstance' " +
"WHERE serverInstance LIKE '$serverInstance' " +
"AND NOT serverInstance LIKE '$newServerInstance'"
)
} while (cursor.moveToNext())
}
}
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "48919e50c8f0abc005d0c05f3c42dfe2",
"entities": [
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `SID` TEXT NOT NULL, `Auth` TEXT NOT NULL, `login` TEXT NOT NULL, `serverInstance` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "SID",
"columnName": "SID",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "Auth",
"columnName": "Auth",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "login",
"columnName": "login",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "serverInstance",
"columnName": "serverInstance",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "articles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `href` TEXT NOT NULL, `categories` TEXT NOT NULL, `author` TEXT NOT NULL, `content` TEXT NOT NULL, `streamId` TEXT NOT NULL, `readStatus` TEXT NOT NULL, `crawled` INTEGER NOT NULL, `published` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "href",
"columnName": "href",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "categories",
"columnName": "categories",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "author",
"columnName": "author",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "content",
"columnName": "content",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "streamId",
"columnName": "streamId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "readStatus",
"columnName": "readStatus",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "crawled",
"columnName": "crawled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "published",
"columnName": "published",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "subscriptions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`imageBitmap` BLOB, `id` TEXT NOT NULL, `title` TEXT NOT NULL, `iconUrl` TEXT NOT NULL, `unreadCount` INTEGER NOT NULL, `subscriptionCategories` TEXT NOT NULL, `newestArticleDate` INTEGER NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "imageBitmap",
"columnName": "imageBitmap",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "iconUrl",
"columnName": "iconUrl",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "unreadCount",
"columnName": "unreadCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscriptionCategories",
"columnName": "subscriptionCategories",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "newestArticleDate",
"columnName": "newestArticleDate",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "subscription_categories",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `label` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"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, \"48919e50c8f0abc005d0c05f3c42dfe2\")"
]
}
}
\ No newline at end of file
......@@ -34,7 +34,7 @@ class InstanceUrl(val protocole: String, base: String) {
override fun toString(): String = "$protocole$base$endpoint"
companion object {
private const val apiEndpoint = "/api/greader.php"
const val apiEndpoint = "/api/greader.php"
val authorizedProtocols = arrayOf("https://", "http://")
fun parse(url: String): InstanceUrl {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment