[CalendarServer-changes] [15704] CalendarServer/trunk/txdav/common/datastore

source_changes at macosforge.org source_changes at macosforge.org
Fri Jun 24 10:30:54 PDT 2016


Revision: 15704
          http://trac.calendarserver.org//changeset/15704
Author:   sagen at apple.com
Date:     2016-06-24 10:30:54 -0700 (Fri, 24 Jun 2016)
Log Message:
-----------
Don't let a failure to recover one bad event prevent others from being recovered from the trash

Modified Paths:
--------------
    CalendarServer/trunk/txdav/common/datastore/sql.py
    CalendarServer/trunk/txdav/common/datastore/test/test_trash.py

Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/sql.py	2016-06-24 17:03:43 UTC (rev 15703)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py	2016-06-24 17:30:54 UTC (rev 15704)
@@ -3763,7 +3763,11 @@
                         summary = component.mainComponent().propertyValue("SUMMARY", "<no title>")
                         print("Recovering \"{}\"".format(summary.encode("utf-8")))
 
-                    yield child.fromTrash()
+                    try:
+                        yield child.fromTrash()
+                    except Exception as e:
+                        # Don't let one failed child stop the others, but alert the user
+                        print("Failed to restore \"{}\" due to {}".format(child.name(), str(e)))
 
 
     def isInTrash(self):

Modified: CalendarServer/trunk/txdav/common/datastore/test/test_trash.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/test_trash.py	2016-06-24 17:03:43 UTC (rev 15703)
+++ CalendarServer/trunk/txdav/common/datastore/test/test_trash.py	2016-06-24 17:30:54 UTC (rev 15704)
@@ -25,6 +25,7 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twistedcaldav.ical import Component
 from twistedcaldav.test.util import StoreTestCase
+from txdav.caldav.datastore.sql import CalendarObject
 from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
 
 
@@ -1813,7 +1814,131 @@
 
         yield txn.commit()
 
+    @inlineCallbacks
+    def test_trashCalendarRestoreWithFailures(self):
 
+        from twistedcaldav.stdconfig import config
+        self.patch(config, "EnableTrashCollection", True)
+
+        txn = self.store.newTransaction()
+
+        collection = yield self._collectionForUser(txn, "user01", "test", create=True)
+
+        data1 = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
+BEGIN:VEVENT
+UID:{uid}
+DTSTART;TZID=America/Los_Angeles:20141108T093000
+DTEND;TZID=America/Los_Angeles:20141108T103000
+CREATED:20141106T192546Z
+DTSTAMP:20141106T192546Z
+RRULE:FREQ=DAILY
+SEQUENCE:0
+SUMMARY:repeating event
+TRANSP:OPAQUE
+END:VEVENT
+BEGIN:VEVENT
+UID:{uid}
+RECURRENCE-ID;TZID=America/Los_Angeles:20141111T093000
+DTSTART;TZID=America/Los_Angeles:20141111T110000
+DTEND;TZID=America/Los_Angeles:20141111T120000
+CREATED:20141106T192546Z
+DTSTAMP:20141106T192546Z
+SEQUENCE:0
+SUMMARY:repeating event
+TRANSP:OPAQUE
+END:VEVENT
+END:VCALENDAR
+"""
+
+        # Create objects
+        yield collection.createObjectResourceWithName(
+            "test1.ics",
+            Component.allFromString(data1.format(uid="1CE3B280-DBC9-4E8E-B0B2-996754020E5F"))
+        )
+        yield collection.createObjectResourceWithName(
+            "test2.ics",
+            Component.allFromString(data1.format(uid="2CE3B280-DBC9-4E8E-B0B2-996754020E5F"))
+        )
+
+        # Two objects in collection
+        objects = yield collection.listObjectResources()
+        self.assertEquals(len(objects), 2)
+
+        # No objects in trash
+        home1 = yield self._homeForUser(txn, "user01")
+        trash1 = yield home1.getTrash(create=True)
+        trash = yield self._collectionForUser(txn, "user01", trash1.name())
+        objects = yield trash.listObjectResources()
+        self.assertEquals(len(objects), 0)
+
+        # # Verify it's not in the trash
+        # for resource in (resource1, resource2):
+        #     self.assertFalse(resource.isInTrash())
+        #     trashed = resource.whenTrashed()
+        #     self.assertTrue(trashed is None)
+
+        # collection = yield self._collectionForUser(txn, "user01", "test")
+        # resources = yield trash.trashForCollection(collection._resourceID)
+        # self.assertEquals(len(resources), 0)
+
+        yield txn.commit()
+
+        txn = self.store.newTransaction()
+        collection = yield self._collectionForUser(txn, "user01", "test")
+        yield collection.remove()
+        yield txn.commit()
+
+        txn = self.store.newTransaction()
+        # Two objects in trash
+        trash = yield self._collectionForUser(txn, "user01", trash1.name())
+        objects = yield trash.listObjectResources()
+        self.assertEquals(len(objects), 2)
+
+
+        resources = yield trash.trashForCollection(collection._resourceID)
+        self.assertEquals(len(resources), 2)
+
+        home = yield self._homeForUser(txn, "user01")
+        names = yield home.listChildren(onlyInTrash=True)
+        trashedName = names[0]
+        collection = yield self._collectionForUser(txn, "user01", trashedName, onlyInTrash=True)
+
+        # Patch CalendarObject.fromTrash to raise an exception for one specific resource
+        originalFromTrash = CalendarObject.fromTrash
+        def fakeFromTrash(self):
+            if self._uid == "2CE3B280-DBC9-4E8E-B0B2-996754020E5F":
+                raise 1 / 0
+            else:
+                return originalFromTrash(self)
+        self.patch(CalendarObject, "fromTrash", fakeFromTrash)
+
+        yield collection.fromTrash()
+
+        yield txn.commit()
+
+        yield JobItem.waitEmpty(self.store.newTransaction, reactor, 60)
+
+        # Now when we restore from trash, since one event failed to restore, we
+        # should still have gotten the other back
+        txn = self.store.newTransaction()
+        home = yield self._homeForUser(txn, "user01")
+        names = yield home.listChildren()
+        self.assertTrue(trashedName in names)
+        names = yield home.listChildren(onlyInTrash=True)
+        self.assertFalse(trashedName in names)
+        resourceNames = yield self._getResourceNames(txn, "user01", trashedName)
+        self.assertEqual(len(resourceNames), 1)
+
+        # Failed event still in the trash
+        trash = yield self._collectionForUser(txn, "user01", trash1.name())
+        objects = yield trash.listObjectResources()
+        self.assertEquals(len(objects), 1)
+
+        yield txn.commit()
+
+
     @inlineCallbacks
     def test_shareeDelete(self):
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20160624/4088637d/attachment.html>


More information about the calendarserver-changes mailing list