diff options
author | Patrick Lang <72232737+patrickunterwegs@users.noreply.github.com> | 2022-02-16 17:05:00 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-16 17:05:00 +0300 |
commit | 15b08ca5ada8dd354e5b4625477982fc2e608c2a (patch) | |
tree | b049a9250fc6620988519f741361f410e60f78e6 | |
parent | 9d57f4c3c454bc0dc2779fa39d61a667a7a383bc (diff) |
#16 store uid correctly when creating related to entries (#17)
* Ignore RELATED-TO when entry links to itself
Closes #14
* Fixed issue with setting a wrong UID that could cause problematic self-references when uploading to the server again
Closes #16
* Fixed test
3 files changed, 158 insertions, 37 deletions
diff --git a/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt b/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt index 807f1bc..d2fad41 100644 --- a/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt +++ b/src/androidTest/java/at/bitfire/ical4android/JtxCollectionTest.kt @@ -93,6 +93,146 @@ class JtxCollectionTest { } @Test + fun updateRelatedTo_check_update_of_linkedId_CHILD_to_PARENT_is_present() { + JtxCollection.create(testAccount, client, cv) + val collections = JtxCollection.find(testAccount, client, context, TestJtxCollection.Factory, null, null) + + // insert 2 icalobjects + val parentCV = ContentValues().apply { + put(JtxContract.JtxICalObject.SUMMARY, "summary") + put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VJOURNAL.name) + put(JtxContract.JtxICalObject.UID, "AAA") + put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id) + } + val parentUri = client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), parentCV) + val childCV = ContentValues().apply { + put(JtxContract.JtxICalObject.SUMMARY, "summary") + put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VJOURNAL.name) + put(JtxContract.JtxICalObject.UID, "BBB") + put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id) + } + val childUri = client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), childCV) + + val icalobjects = collections[0].queryICalObjects(null, null) + assertEquals(2, icalobjects.size) + + // link one of them to the other with PARENT reltype + val parentRelCV = ContentValues().apply { + put(JtxContract.JtxRelatedto.ICALOBJECT_ID, childUri?.lastPathSegment) + put(JtxContract.JtxRelatedto.TEXT, "AAA") + put(JtxContract.JtxRelatedto.RELTYPE, JtxContract.JtxRelatedto.Reltype.PARENT.name) + } + client.insert(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), parentRelCV) + + // update related to and check + collections[0].updateRelatedTo() + + // check child to parent + client.query( + JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), + arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.TEXT, JtxContract.JtxRelatedto.RELTYPE), + "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ?", + arrayOf(childUri?.lastPathSegment), + null + ).use { + assertNotNull(it) + assertEquals(1, it?.count) + it?.moveToFirst() + assertEquals(childUri?.lastPathSegment?.toLong(), it?.getLong(0)) // ICALOBJECT_ID + assertEquals(parentUri?.lastPathSegment?.toLong(), it?.getLong(1)) // LINKEDICALOBJECT_ID + assertEquals("AAA", it?.getString(2)) // TEXT (UID) + assertEquals(JtxContract.JtxRelatedto.Reltype.PARENT.name, it?.getString(3)) + } + + // check parent to child + client.query( + JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), + arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.TEXT, JtxContract.JtxRelatedto.RELTYPE), + "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ?", + arrayOf(parentUri?.lastPathSegment), + null + ).use { + assertNotNull(it) + assertEquals(1, it?.count) + it?.moveToFirst() + assertEquals(parentUri?.lastPathSegment?.toLong(), it?.getLong(0)) // ICALOBJECT_ID + assertEquals(childUri?.lastPathSegment?.toLong(), it?.getLong(1)) // LINKEDICALOBJECT_ID + assertEquals("BBB", it?.getString(2)) // TEXT (UID) + assertEquals(JtxContract.JtxRelatedto.Reltype.CHILD.name, it?.getString(3)) + } + } + + @Test + fun updateRelatedTo_check_update_of_linkedId_PARENT_TO_CHILD_is_present() { + JtxCollection.create(testAccount, client, cv) + val collections = JtxCollection.find(testAccount, client, context, TestJtxCollection.Factory, null, null) + + // insert 2 icalobjects + val parentCV = ContentValues().apply { + put(JtxContract.JtxICalObject.SUMMARY, "summary") + put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VJOURNAL.name) + put(JtxContract.JtxICalObject.UID, "AAA") + put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id) + } + val parentUri = client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), parentCV) + val childCV = ContentValues().apply { + put(JtxContract.JtxICalObject.SUMMARY, "summary") + put(JtxContract.JtxICalObject.COMPONENT, JtxContract.JtxICalObject.Component.VJOURNAL.name) + put(JtxContract.JtxICalObject.UID, "BBB") + put(JtxContract.JtxICalObject.ICALOBJECT_COLLECTIONID, collections[0].id) + } + val childUri = client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), childCV) + + val icalobjects = collections[0].queryICalObjects(null, null) + assertEquals(2, icalobjects.size) + + // link one of them to the other with PARENT reltype + val parent2childRelCV = ContentValues().apply { + put(JtxContract.JtxRelatedto.ICALOBJECT_ID, parentUri?.lastPathSegment) + put(JtxContract.JtxRelatedto.TEXT, "BBB") + put(JtxContract.JtxRelatedto.RELTYPE, JtxContract.JtxRelatedto.Reltype.CHILD.name) + } + client.insert(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), parent2childRelCV) + + // update related to and check + collections[0].updateRelatedTo() + + // check child to parent + client.query( + JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), + arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.TEXT, JtxContract.JtxRelatedto.RELTYPE), + "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ?", + arrayOf(parentUri?.lastPathSegment), + null + ).use { + assertNotNull(it) + assertEquals(1, it?.count) + it?.moveToFirst() + assertEquals(parentUri?.lastPathSegment?.toLong(), it?.getLong(0)) // ICALOBJECT_ID + assertEquals(childUri?.lastPathSegment?.toLong(), it?.getLong(1)) // LINKEDICALOBJECT_ID + assertEquals("BBB", it?.getString(2)) // TEXT (UID) + assertEquals(JtxContract.JtxRelatedto.Reltype.CHILD.name, it?.getString(3)) + } + + // check parent to child + client.query( + JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(testAccount), + arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.TEXT, JtxContract.JtxRelatedto.RELTYPE), + "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ?", + arrayOf(childUri?.lastPathSegment), + null + ).use { + assertNotNull(it) + assertEquals(1, it?.count) + it?.moveToFirst() + assertEquals(childUri?.lastPathSegment?.toLong(), it?.getLong(0)) // ICALOBJECT_ID + assertEquals(parentUri?.lastPathSegment?.toLong(), it?.getLong(1)) // LINKEDICALOBJECT_ID + assertEquals("AAA", it?.getString(2)) // TEXT (UID) + assertEquals(JtxContract.JtxRelatedto.Reltype.PARENT.name, it?.getString(3)) + } + } + + @Test fun getICSForCollection_test() { val collectionUri = JtxCollection.create(testAccount, client, cv) assertNotNull(collectionUri) @@ -115,7 +255,6 @@ class JtxCollectionTest { client.insert(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(testAccount), cv2) val ics = collections[0].getICSForCollection() - assertTrue(ics.contains(Regex("BEGIN:VCALENDAR(\\n*|\\r*|\\t*|.*)*END:VCALENDAR"))) assertTrue(ics.contains("PRODID:+//IDN bitfire.at//ical4android")) assertTrue(ics.contains("SUMMARY:summary")) diff --git a/src/main/java/at/bitfire/ical4android/JtxCollection.kt b/src/main/java/at/bitfire/ical4android/JtxCollection.kt index 25277b9..fe7515d 100644 --- a/src/main/java/at/bitfire/ical4android/JtxCollection.kt +++ b/src/main/java/at/bitfire/ical4android/JtxCollection.kt @@ -175,18 +175,8 @@ open class JtxCollection<out T: JtxICalObject>(val account: Account, val idOfthisUid = idOfthisUidCursor.getLong(0) val updateContentValues = ContentValues() - updateContentValues.put( - JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, - idOfthisUid - ) - - client.update( - JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter( - account - ), - updateContentValues, - "${JtxContract.JtxRelatedto.TEXT} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ?", - arrayOf(uid2upddate, "0") + updateContentValues.put(JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, idOfthisUid) + client.update(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), updateContentValues,"${JtxContract.JtxRelatedto.TEXT} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ?", arrayOf(uid2upddate, "0") ) } } @@ -198,27 +188,24 @@ open class JtxCollection<out T: JtxICalObject>(val account: Account, client.query(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.RELTYPE), "${JtxContract.JtxRelatedto.RELTYPE} = ?", arrayOf(JtxContract.JtxRelatedto.Reltype.PARENT.name), null).use { cursorAllLinkedParents -> while (cursorAllLinkedParents?.moveToNext() == true) { - val icalObjectId = cursorAllLinkedParents.getString(0) - val linkedIcalObjectId = cursorAllLinkedParents.getString(1) + val childId = cursorAllLinkedParents.getString(0) + val parentId = cursorAllLinkedParents.getString(1) - client.query(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.RELTYPE), "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.RELTYPE} = ?", arrayOf(linkedIcalObjectId.toString(), icalObjectId.toString(), JtxContract.JtxRelatedto.Reltype.CHILD.name), null).use { cursor -> + client.query(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.RELTYPE), "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.RELTYPE} = ?", arrayOf(parentId.toString(), childId.toString(), JtxContract.JtxRelatedto.Reltype.CHILD.name), null).use { cursor -> // if the query does not bring any result, then we insert the opposite relationship if (cursor?.moveToFirst() == false) { - //get the UID of the linked entry - client.query(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxICalObject.UID), "${JtxContract.JtxICalObject.ID} = ?", arrayOf(linkedIcalObjectId.toString()), null).use { + client.query(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxICalObject.UID), "${JtxContract.JtxICalObject.ID} = ?", arrayOf(childId.toString()), null).use { foundIcalObjectCursor -> if (foundIcalObjectCursor?.moveToFirst() == true) { - val uid = foundIcalObjectCursor.getString(0) - + val childUID = foundIcalObjectCursor.getString(0) val cv = ContentValues().apply { - put(JtxContract.JtxRelatedto.ICALOBJECT_ID, linkedIcalObjectId) - put(JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, icalObjectId) + put(JtxContract.JtxRelatedto.ICALOBJECT_ID, parentId) + put(JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, childId) put(JtxContract.JtxRelatedto.RELTYPE, JtxContract.JtxRelatedto.Reltype.CHILD.name) - put(JtxContract.JtxRelatedto.TEXT, uid) + put(JtxContract.JtxRelatedto.TEXT, childUID) } - client.insert(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), cv) } } @@ -233,30 +220,27 @@ open class JtxCollection<out T: JtxICalObject>(val account: Account, cursorAllLinkedParents -> while (cursorAllLinkedParents?.moveToNext() == true) { - val icalObjectId = cursorAllLinkedParents.getLong(0) - val linkedIcalObjectId = cursorAllLinkedParents.getLong(1) + val parentId = cursorAllLinkedParents.getLong(0) + val childId = cursorAllLinkedParents.getLong(1) - client.query(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.RELTYPE), "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.RELTYPE} = ?", arrayOf(linkedIcalObjectId.toString(), icalObjectId.toString(), JtxContract.JtxRelatedto.Reltype.PARENT.name), null).use { + client.query(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxRelatedto.ICALOBJECT_ID, JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, JtxContract.JtxRelatedto.RELTYPE), "${JtxContract.JtxRelatedto.ICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID} = ? AND ${JtxContract.JtxRelatedto.RELTYPE} = ?", arrayOf(childId.toString(), parentId.toString(), JtxContract.JtxRelatedto.Reltype.PARENT.name), null).use { cursor -> // if the query does not bring any result, then we insert the opposite relationship if (cursor?.moveToFirst() == false) { //get the UID of the linked entry - client.query(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxICalObject.UID), "${JtxContract.JtxICalObject.ID} = ?", arrayOf(linkedIcalObjectId.toString()), null).use { + client.query(JtxContract.JtxICalObject.CONTENT_URI.asSyncAdapter(account), arrayOf(JtxContract.JtxICalObject.UID), "${JtxContract.JtxICalObject.ID} = ?", arrayOf(parentId.toString()), null).use { foundIcalObjectCursor -> if(foundIcalObjectCursor?.moveToFirst() == true) { - - val uid = foundIcalObjectCursor.getString(0) - + val parentUID = foundIcalObjectCursor.getString(0) val cv = ContentValues().apply { - put(JtxContract.JtxRelatedto.ICALOBJECT_ID, linkedIcalObjectId) - put(JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, icalObjectId) + put(JtxContract.JtxRelatedto.ICALOBJECT_ID, childId) + put(JtxContract.JtxRelatedto.LINKEDICALOBJECT_ID, parentId) put(JtxContract.JtxRelatedto.RELTYPE, JtxContract.JtxRelatedto.Reltype.PARENT.name) - put(JtxContract.JtxRelatedto.TEXT, uid) + put(JtxContract.JtxRelatedto.TEXT, parentUID) } - client.insert(JtxContract.JtxRelatedto.CONTENT_URI.asSyncAdapter(account), cv) } } @@ -266,7 +250,6 @@ open class JtxCollection<out T: JtxICalObject>(val account: Account, } } - /** * @return a string with all JtxICalObjects within the collection as iCalendar */ diff --git a/src/main/java/at/bitfire/ical4android/JtxICalObject.kt b/src/main/java/at/bitfire/ical4android/JtxICalObject.kt index 087ca00..054fde1 100644 --- a/src/main/java/at/bitfire/ical4android/JtxICalObject.kt +++ b/src/main/java/at/bitfire/ical4android/JtxICalObject.kt @@ -1209,7 +1209,6 @@ duration?.let(props::add) ) } - this.relatedTo.forEach { related -> val relatedToContentValues = ContentValues().apply { put(JtxContract.JtxRelatedto.ICALOBJECT_ID, id) |