Separate principal and principal_access tables

pull/1408/head
Alex Baker 3 years ago
parent 14cf7b39ec
commit 3d980ebc59

File diff suppressed because it is too large Load Diff

@ -65,10 +65,10 @@ class SharingMailboxDotOrgTest : CaldavTest() {
val principal = principalDao.getAll().first()
assertEquals(calendar.id, principal.list)
assertEquals("/principals/users/5", principal.principal)
assertEquals("/principals/users/5", principal.href)
assertNull(principal.displayName)
assertEquals(CaldavCalendar.INVITE_ACCEPTED, principal.inviteStatus)
assertEquals(CaldavCalendar.ACCESS_UNKNOWN, principal.access)
assertEquals(CaldavCalendar.ACCESS_UNKNOWN, principal.access.access)
}
companion object {

@ -80,10 +80,10 @@ class SharingOwncloudTest : CaldavTest() {
.first()
assertEquals(calendar.id, principal.list)
assertEquals("principal:principals/users/user2", principal.principal)
assertEquals("user2", principal.displayName)
assertEquals("principal:principals/users/user2", principal.href)
assertEquals("user2", principal.name)
assertEquals(CaldavCalendar.INVITE_ACCEPTED, principal.inviteStatus)
assertEquals(CaldavCalendar.ACCESS_READ_ONLY, principal.access)
assertEquals(ACCESS_READ_ONLY, principal.access.access)
}
@Test
@ -104,10 +104,10 @@ class SharingOwncloudTest : CaldavTest() {
.first()
assertEquals(calendar.id, principal.list)
assertEquals("principals/users/user1", principal.principal)
assertEquals("principals/users/user1", principal.href)
assertEquals(null, principal.displayName)
assertEquals(CaldavCalendar.INVITE_ACCEPTED, principal.inviteStatus)
assertEquals(CaldavCalendar.ACCESS_OWNER, principal.access)
assertEquals(ACCESS_OWNER, principal.access.access)
}
companion object {

@ -102,10 +102,10 @@ class SharingSabredavTest : CaldavTest() {
val principal = principalDao.getAll().first()
assertEquals(calendar.id, principal.list)
assertEquals("mailto:user@example.com", principal.principal)
assertEquals("mailto:user@example.com", principal.href)
assertEquals("Example User", principal.displayName)
assertEquals(INVITE_ACCEPTED, principal.inviteStatus)
assertEquals(ACCESS_READ_WRITE, principal.access)
assertEquals(ACCESS_READ_WRITE, principal.access.access)
}
@Test
@ -126,10 +126,10 @@ class SharingSabredavTest : CaldavTest() {
.first()
assertEquals(calendar.id, principal.list)
assertEquals("/principals/user1", principal.principal)
assertEquals("/principals/user1", principal.href)
assertEquals(null, principal.displayName)
assertEquals(INVITE_ACCEPTED, principal.inviteStatus)
assertEquals(ACCESS_OWNER, principal.access)
assertEquals(ACCESS_OWNER, principal.access.access)
}
companion object {

@ -3,8 +3,39 @@ package com.todoroo.astrid.dao
import androidx.room.Database
import androidx.room.RoomDatabase
import com.todoroo.astrid.data.Task
import org.tasks.data.*
import org.tasks.data.Alarm
import org.tasks.data.AlarmDao
import org.tasks.data.CaldavAccount
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavDao
import org.tasks.data.CaldavTask
import org.tasks.data.ContentProviderDao
import org.tasks.data.DeletionDao
import org.tasks.data.Filter
import org.tasks.data.FilterDao
import org.tasks.data.Geofence
import org.tasks.data.GoogleTask
import org.tasks.data.GoogleTaskAccount
import org.tasks.data.GoogleTaskDao
import org.tasks.data.GoogleTaskList
import org.tasks.data.GoogleTaskListDao
import org.tasks.data.LocationDao
import org.tasks.data.Place
import org.tasks.data.Principal
import org.tasks.data.PrincipalAccess
import org.tasks.data.PrincipalDao
import org.tasks.data.Tag
import org.tasks.data.TagDao
import org.tasks.data.TagData
import org.tasks.data.TagDataDao
import org.tasks.data.TaskAttachment
import org.tasks.data.TaskAttachmentDao
import org.tasks.data.TaskDao
import org.tasks.data.TaskListMetadata
import org.tasks.data.TaskListMetadataDao
import org.tasks.data.UpgraderDao
import org.tasks.data.UserActivity
import org.tasks.data.UserActivityDao
import org.tasks.notifications.Notification
import org.tasks.notifications.NotificationDao
@ -28,8 +59,9 @@ import org.tasks.notifications.NotificationDao
CaldavAccount::class,
GoogleTaskAccount::class,
Principal::class,
PrincipalAccess::class
],
version = 79)
version = 80)
abstract class Database : RoomDatabase() {
abstract fun notificationDao(): NotificationDao
abstract val tagDataDao: TagDataDao

@ -24,8 +24,8 @@ import org.tasks.data.CaldavAccount.Companion.SERVER_TASKS
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_OWNER
import org.tasks.data.Principal
import org.tasks.data.Principal.Companion.name
import org.tasks.data.PrincipalDao
import org.tasks.data.PrincipalWithAccess
import javax.inject.Inject
@AndroidEntryPoint
@ -94,7 +94,7 @@ class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
private val canRemovePrincipals: Boolean
get() = caldavCalendar?.access == ACCESS_OWNER && caldavAccount.canRemovePrincipal
private fun onRemove(principal: Principal) {
private fun onRemove(principal: PrincipalWithAccess) {
if (requestInProgress()) {
return
}
@ -106,7 +106,7 @@ class CaldavCalendarSettingsActivity : BaseCaldavCalendarSettingsActivity() {
.show()
}
private fun removePrincipal(principal: Principal) = lifecycleScope.launch {
private fun removePrincipal(principal: PrincipalWithAccess) = lifecycleScope.launch {
try {
viewModel.removeUser(caldavAccount, caldavCalendar!!, principal)
} catch (e: Exception) {

@ -17,8 +17,8 @@ import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.ACCESS_READ_WRITE
import org.tasks.data.CaldavCalendar.Companion.INVITE_UNKNOWN
import org.tasks.data.CaldavDao
import org.tasks.data.Principal
import org.tasks.data.PrincipalDao
import org.tasks.data.PrincipalWithAccess
import org.tasks.sync.SyncAdapters
import timber.log.Timber
import javax.inject.Inject
@ -103,21 +103,17 @@ class CaldavCalendarViewModel @Inject constructor(
withContext(Dispatchers.IO) {
provider.forAccount(account, list.url!!).share(account, href)
}
principalDao.insert(Principal().apply {
this.list = list.id
principal = href
inviteStatus = INVITE_UNKNOWN
access = ACCESS_READ_WRITE
})
val principal = principalDao.getOrCreatePrincipal(account, href)
principalDao.getOrCreateAccess(list, principal, INVITE_UNKNOWN, ACCESS_READ_WRITE)
syncAdapters.sync(true)
}
suspend fun removeUser(account: CaldavAccount, list: CaldavCalendar, principal: Principal) =
suspend fun removeUser(account: CaldavAccount, list: CaldavCalendar, principal: PrincipalWithAccess) =
doRequest {
withContext(Dispatchers.IO) {
provider.forAccount(account).removePrincipal(account, list, principal)
provider.forAccount(account).removePrincipal(account, list, principal.href)
}
principalDao.delete(principal)
principalDao.delete(principal.access)
}
private suspend fun <T> doRequest(action: suspend () -> T): T? =

@ -43,7 +43,6 @@ import org.tasks.data.CaldavAccount.Companion.SERVER_OWNCLOUD
import org.tasks.data.CaldavAccount.Companion.SERVER_SABREDAV
import org.tasks.data.CaldavAccount.Companion.SERVER_TASKS
import org.tasks.data.CaldavCalendar
import org.tasks.data.Principal
import org.tasks.ui.DisplayableException
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory
@ -277,37 +276,37 @@ open class CaldavClient(
suspend fun removePrincipal(
account: CaldavAccount,
calendar: CaldavCalendar,
principal: Principal,
href: String,
) {
when (account.serverType) {
SERVER_TASKS, SERVER_SABREDAV -> removeSabrePrincipal(calendar, principal)
SERVER_OWNCLOUD -> removeOwncloudPrincipal(calendar, principal)
SERVER_TASKS, SERVER_SABREDAV -> removeSabrePrincipal(calendar, href)
SERVER_OWNCLOUD -> removeOwncloudPrincipal(calendar, href)
else -> throw IllegalArgumentException()
}
}
private suspend fun removeOwncloudPrincipal(calendar: CaldavCalendar, principal: Principal) =
private suspend fun removeOwncloudPrincipal(calendar: CaldavCalendar, href: String) =
withContext(Dispatchers.IO) {
DavCollection(httpClient, calendar.url!!.toHttpUrl())
.post(
"""
<x4:share xmlns:x4="$NS_OWNCLOUD">
<x4:remove>
<x0:href xmlns:x0="$NS_WEBDAV">${principal.principal}</x0:href>
<x0:href xmlns:x0="$NS_WEBDAV">$href</x0:href>
</x4:remove>
</x4:share>
""".trimIndent().toRequestBody(MIME_XML)
) {}
}
private suspend fun removeSabrePrincipal(calendar: CaldavCalendar, principal: Principal) =
private suspend fun removeSabrePrincipal(calendar: CaldavCalendar, href: String) =
withContext(Dispatchers.IO) {
DavCollection(httpClient, calendar.url!!.toHttpUrl())
.post(
"""
<D:share-resource xmlns:D="$NS_WEBDAV">
<D:sharee>
<D:href>${principal.principal}</D:href>
<D:href>$href</D:href>
<D:share-access>
<D:no-access />
</D:share-access>

@ -180,12 +180,8 @@ class CaldavSynchronizer @Inject constructor(
localBroadcastManager.broadcastRefreshList()
}
resource
.principals
.onEach { it.list = calendar.id }
.let {
principalDao.deleteRemoved(calendar.id, it.mapNotNull { p -> p.principal } )
principalDao.insert(it)
}
.principals(account, calendar)
.let { principalDao.deleteRemoved(calendar.id, it.map(PrincipalAccess::id)) }
sync(calendar, resource, caldavClient.httpClient)
}
setError(account, "")
@ -345,6 +341,57 @@ class CaldavSynchronizer @Inject constructor(
Timber.d("SENT %s", caldavTask)
}
fun Response.principals(
account: CaldavAccount,
list: CaldavCalendar
): List<PrincipalAccess> {
val access = ArrayList<PrincipalAccess>()
this[Invite::class.java]
?.sharees
?.filter { it.href?.let { href -> !isCurrentUser(href) } ?: false }
?.map {
val principal = principalDao.getOrCreatePrincipal(
account,
it.href!!,
it.properties
.find { p -> p is DisplayName }
?.let { name -> (name as DisplayName).displayName }
)
principalDao.getOrCreateAccess(
list,
principal,
invite = it.response?.toStatus ?: INVITE_UNKNOWN,
access = it.access?.access?.toAccess ?: ACCESS_UNKNOWN
)
}
?.let { access.addAll(it) }
this[OCInvite::class.java]?.users
?.map {
val principal = principalDao.getOrCreatePrincipal(account, it.href)
principalDao.getOrCreateAccess(
list,
principal,
it.response.toStatus,
it.access.access.toAccess
)
}
?.let {
if (!isOwncloudOwner) {
val principal = principalDao.getOrCreatePrincipal(
account, this@principals[OCOwnerPrincipal::class.java]?.owner!!
)
access.add(principalDao.getOrCreateAccess(
list,
principal,
INVITE_ACCEPTED,
ACCESS_OWNER
))
}
access.addAll(it)
}
return access
}
companion object {
init {
prodId = ProdId("+//IDN tasks.org//android-" + BuildConfig.VERSION_CODE + "//EN")
@ -387,45 +434,6 @@ class CaldavSynchronizer @Inject constructor(
private fun Response.isCurrentUser(href: String) =
this[CurrentUserPrincipal::class.java]?.href?.endsWith("$href/") == true
val Response.principals: List<Principal>
get() {
val principals = ArrayList<Principal>()
this[Invite::class.java]
?.sharees
?.filter { it.href?.let { href -> !isCurrentUser(href) } ?: false }
?.map {
Principal().apply {
principal = it.href
it.properties.find { it is DisplayName }?.let { name ->
displayName = (name as DisplayName).displayName
}
inviteStatus = it.response?.toStatus ?: INVITE_UNKNOWN
access = it.access?.access?.toAccess ?: ACCESS_UNKNOWN
}
}
?.let { principals.addAll(it) }
this[OCInvite::class.java]?.users
?.map {
Principal().apply {
principal = it.href
displayName = it.commonName
inviteStatus = it.response.toStatus
access = it.access.access.toAccess
}
}
?.let {
if (!isOwncloudOwner) {
principals.add(Principal().apply {
principal = this@principals[OCOwnerPrincipal::class.java]?.owner
inviteStatus = INVITE_ACCEPTED
access = ACCESS_OWNER
})
}
principals.addAll(it)
}
return principals
}
private val Property.Name.toAccess: Int
get() = when (this) {
SHARED_OWNER, OCAccess.SHARED_OWNER -> ACCESS_OWNER

@ -29,20 +29,24 @@ import org.tasks.R
import org.tasks.compose.Constants.HALF_KEYLINE
import org.tasks.compose.Constants.ICON_ALPHA
import org.tasks.compose.Constants.KEYLINE_FIRST
import org.tasks.data.CaldavCalendar
import org.tasks.data.CaldavCalendar.Companion.INVITE_ACCEPTED
import org.tasks.data.CaldavCalendar.Companion.INVITE_DECLINED
import org.tasks.data.CaldavCalendar.Companion.INVITE_INVALID
import org.tasks.data.CaldavCalendar.Companion.INVITE_NO_RESPONSE
import org.tasks.data.CaldavCalendar.Companion.INVITE_UNKNOWN
import org.tasks.data.Principal
import org.tasks.data.Principal.Companion.name
import org.tasks.data.PrincipalAccess
import org.tasks.data.PrincipalWithAccess
private val principals = listOf(
Principal().apply {
displayName = "user1"
inviteStatus = INVITE_INVALID
},
Principal().apply { displayName = "a really really really really really long display name" },
PrincipalWithAccess(
PrincipalAccess(list = 0, invite = INVITE_INVALID),
Principal(account = 0, href = "", displayName = "user1")
),
PrincipalWithAccess(
PrincipalAccess(list = 0, invite = INVITE_ACCEPTED),
Principal(account = 0, href = "", displayName = "a really really really really really long display name")
)
)
@Preview(showBackground = true, backgroundColor = 0xFFFFFF)
@ -66,8 +70,8 @@ private fun NotOwner() = MaterialTheme {
object ListSettingsComposables {
@Composable
fun PrincipalList(
principals: List<Principal>,
onRemove: ((Principal) -> Unit)?,
principals: List<PrincipalWithAccess>,
onRemove: ((PrincipalWithAccess) -> Unit)?,
) {
Column(
modifier = Modifier
@ -88,8 +92,8 @@ object ListSettingsComposables {
@Composable
fun PrincipalRow(
principal: Principal,
onRemove: ((Principal) -> Unit)?,
principal: PrincipalWithAccess,
onRemove: ((PrincipalWithAccess) -> Unit)?,
) {
Row(
Modifier
@ -121,7 +125,7 @@ object ListSettingsComposables {
style = MaterialTheme.typography.body1,
color = colors.onBackground,
)
if (principal.inviteStatus != CaldavCalendar.INVITE_ACCEPTED) {
if (principal.inviteStatus != INVITE_ACCEPTED) {
Text(
stringResource(when (principal.inviteStatus) {
INVITE_UNKNOWN, INVITE_NO_RESPONSE ->

@ -243,13 +243,13 @@ SELECT EXISTS(SELECT 1
abstract suspend fun getCalendars(tasks: List<Long>): List<String>
@Query("""
SELECT caldav_lists.*, COUNT(DISTINCT(tasks._id)) AS count, COUNT(DISTINCT(principals.principal_id)) AS principals
SELECT caldav_lists.*, COUNT(DISTINCT(tasks._id)) AS count, COUNT(DISTINCT(principal_access.id)) AS principals
FROM caldav_lists
LEFT JOIN caldav_tasks
ON caldav_tasks.cd_calendar = caldav_lists.cdl_uuid
LEFT JOIN tasks ON caldav_tasks.cd_task = tasks._id AND tasks.deleted = 0 AND tasks.completed = 0 AND
tasks.hideUntil < :now AND cd_deleted = 0
LEFT JOIN principals ON caldav_lists.cdl_id = principals.principal_list
LEFT JOIN principal_access ON caldav_lists.cdl_id = principal_access.list
WHERE caldav_lists.cdl_account = :uuid
GROUP BY caldav_lists.cdl_uuid
""")

@ -1,76 +1,38 @@
package org.tasks.data
import androidx.room.*
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(
tableName = "principals",
foreignKeys = [ForeignKey(
entity = CaldavCalendar::class,
parentColumns = arrayOf("cdl_id"),
childColumns = arrayOf("principal_list"),
onDelete = ForeignKey.CASCADE
)],
indices = [Index(value = ["principal_list", "principal"], unique = true)]
foreignKeys = [
ForeignKey(
entity = CaldavAccount::class,
parentColumns = arrayOf("cda_id"),
childColumns = arrayOf("account"),
onDelete = ForeignKey.CASCADE
)
],
indices = [Index(value = ["account", "href"], unique = true)]
)
class Principal {
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "principal_id")
@Transient
var id: Long = 0
@ColumnInfo(name = "principal_list")
var list: Long = 0
@ColumnInfo(name = "principal")
var principal: String? = null
@ColumnInfo(name = "display_name")
var displayName: String? = null
@ColumnInfo(name = "invite")
var inviteStatus: Int = CaldavCalendar.INVITE_UNKNOWN
@ColumnInfo(name = "access")
var access: Int = CaldavCalendar.ACCESS_UNKNOWN
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Principal
if (id != other.id) return false
if (list != other.list) return false
if (principal != other.principal) return false
if (displayName != other.displayName) return false
if (inviteStatus != other.inviteStatus) return false
if (access != other.access) return false
return true
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + list.hashCode()
result = 31 * result + (principal?.hashCode() ?: 0)
result = 31 * result + (displayName?.hashCode() ?: 0)
result = 31 * result + inviteStatus
result = 31 * result + access
return result
}
override fun toString(): String {
return "Principal(id=$id, list=$list, principal=$principal, displayName=$displayName, inviteStatus=$inviteStatus, access=$access)"
}
data class Principal(
@PrimaryKey(autoGenerate = true) var id: Long = 0,
val account: Long,
val href: String,
var email: String? = null,
@ColumnInfo(name = "display_name") var displayName: String? = null
) {
val name: String
get() = displayName
?: href
.replace(MAILTO, "")
.replaceFirst(LAST_SEGMENT, "$1")
companion object {
private val MAILTO = "^mailto:".toRegex()
private val LAST_SEGMENT = ".*/([^/]+).*".toRegex()
val Principal.name: String?
get() = displayName
?: principal
?.replace(MAILTO, "")
?.replaceFirst(LAST_SEGMENT, "$1")
}
}

@ -0,0 +1,36 @@
package org.tasks.data
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import org.tasks.data.CaldavCalendar.Companion.ACCESS_UNKNOWN
import org.tasks.data.CaldavCalendar.Companion.INVITE_UNKNOWN
@Entity(
tableName = "principal_access",
foreignKeys = [
ForeignKey(
entity = Principal::class,
parentColumns = arrayOf("id"),
childColumns = arrayOf("principal"),
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = CaldavCalendar::class,
parentColumns = arrayOf("cdl_id"),
childColumns = arrayOf("list"),
onDelete = ForeignKey.CASCADE
)],
indices = [
Index(value = ["list", "principal"], unique = true),
Index(value = ["principal"])
]
)
data class PrincipalAccess(
@PrimaryKey(autoGenerate = true) var id: Long = 0,
val principal: Long = 0,
val list: Long,
var invite: Int = INVITE_UNKNOWN,
var access: Int = ACCESS_UNKNOWN
)

@ -4,33 +4,68 @@ import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
@Dao
interface PrincipalDao {
@Insert
fun insert(principal: Principal)
fun insert(principal: Principal): Long
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(principals: List<Principal>)
@Insert
fun insert(access: PrincipalAccess): Long
@Update
fun update(access: PrincipalAccess)
@Query("""
DELETE
FROM principals
WHERE principal_list = :list
AND principal NOT IN (:principals)""")
fun deleteRemoved(list: Long, principals: List<String>)
FROM principal_access
WHERE list = :list
AND id NOT IN (:access)""")
fun deleteRemoved(list: Long, access: List<Long>)
@Delete
fun delete(principal: Principal)
fun delete(access: PrincipalAccess)
@Delete
fun delete(principals: List<Principal>)
@Transaction
@Query("SELECT * FROM principal_access")
fun getAll(): List<PrincipalWithAccess>
fun getOrCreatePrincipal(account: CaldavAccount, href: String, displayName: String? = null) =
findPrincipal(account.id, href)
?: Principal(account = account.id, href = href, displayName = displayName)
.apply { id = insert(this) }
fun getOrCreateAccess(
calendar: CaldavCalendar,
principal: Principal,
invite: Int,
access: Int,
): PrincipalAccess =
findAccess(calendar.id, principal.id)
?.apply {
if (this.access != access || this.invite != invite) {
this.access = access
this.invite = invite
update(this)
}
}
?: PrincipalAccess(
principal = principal.id,
list = calendar.id,
invite = invite,
access = access
).apply { id = insert(this) }
@Query("SELECT * FROM principals WHERE account = :account AND href = :href")
fun findPrincipal(account: Long, href: String): Principal?
@Query("SELECT * FROM principals")
fun getAll(): List<Principal>
@Query("SELECT * FROM principal_access WHERE list = :list and principal = :principal")
fun findAccess(list: Long, principal: Long): PrincipalAccess?
@Query("SELECT * FROM principals WHERE principal_list = :id")
fun getPrincipals(id: Long): LiveData<List<Principal>>
@Transaction
@Query("SELECT * FROM principal_access WHERE list = :id")
fun getPrincipals(id: Long): LiveData<List<PrincipalWithAccess>>
}

@ -0,0 +1,19 @@
package org.tasks.data
import androidx.room.Embedded
import androidx.room.Relation
data class PrincipalWithAccess(
@Embedded val access: PrincipalAccess,
@Relation(
parentColumn = "principal",
entityColumn = "id"
)
val principal: Principal
) {
val displayName get() = principal.displayName
val list get() = access.list
val href get() = principal.href
val inviteStatus get() = access.invite
val name get() = principal.name
}

@ -383,6 +383,27 @@ object Migrations {
}
}
private val MIGRATION_79_80 = object : Migration(79, 80) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE `principals`")
database.execSQL(
"CREATE TABLE IF NOT EXISTS `principals` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` INTEGER NOT NULL, `href` TEXT NOT NULL, `email` TEXT, `display_name` TEXT, FOREIGN KEY(`account`) REFERENCES `caldav_accounts`(`cda_id`) ON UPDATE NO ACTION ON DELETE CASCADE )"
)
database.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_principals_account_href` ON `principals` (`account`, `href`)"
)
database.execSQL(
"CREATE TABLE IF NOT EXISTS `principal_access` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `principal` INTEGER NOT NULL, `list` INTEGER NOT NULL, `invite` INTEGER NOT NULL, `access` INTEGER NOT NULL, FOREIGN KEY(`principal`) REFERENCES `principals`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`list`) REFERENCES `caldav_lists`(`cdl_id`) ON UPDATE NO ACTION ON DELETE CASCADE )"
)
database.execSQL(
"CREATE UNIQUE INDEX IF NOT EXISTS `index_principal_access_list_principal` ON `principal_access` (`list`, `principal`)"
)
database.execSQL(
"CREATE INDEX IF NOT EXISTS `index_principal_access_principal` ON `principal_access` (`principal`)"
)
}
}
val MIGRATIONS = arrayOf(
MIGRATION_35_36,
MIGRATION_36_37,
@ -419,6 +440,7 @@ object Migrations {
MIGRATION_76_77,
MIGRATION_77_78,
MIGRATION_78_79,
MIGRATION_79_80,
)
private fun noop(from: Int, to: Int): Migration = object : Migration(from, to) {

@ -2,32 +2,25 @@ package org.tasks.data
import org.junit.Assert.assertEquals
import org.junit.Test
import org.tasks.data.Principal.Companion.name
class PrincipalTest {
@Test
fun lastSegmentTrailingSlash() {
val principal = Principal().apply {
principal = "principals/users/user1/"
}
val principal = Principal(account = 0, href = "/principals/users/user1")
assertEquals("user1", principal.name)
}
@Test
fun lastSegmentNoTrailingSlash() {
val principal = Principal().apply {
principal = "principals/users/user1"
}
val principal = Principal(account = 0, href = "principals/users/user1")
assertEquals("user1", principal.name)
}
@Test
fun stripMailto() {
val principal = Principal().apply {
principal = "mailto:user@example.com"
}
val principal = Principal(account = 0, href = "mailto:user@example.com")
assertEquals("user@example.com", principal.name)
}

Loading…
Cancel
Save