[CalendarServer-changes] [10117] CalendarServer/branches/users/gaya/sharedgroups/txdav/carddav/ datastore

source_changes at macosforge.org source_changes at macosforge.org
Sun Dec 2 13:13:11 PST 2012


Revision: 10117
          http://trac.calendarserver.org//changeset/10117
Author:   gaya at apple.com
Date:     2012-12-02 13:13:11 -0800 (Sun, 02 Dec 2012)
Log Message:
-----------
generate group vCard from db to fix txdav.carddav.datastore.test.test_sql.AddressBookSQLStorageTests.test_putConcurrency.

Modified Paths:
--------------
    CalDAVTester/branches/users/gaya/sharedgroupstester/Resource/CardDAV/sharing/addressbooks/group/13.vcf
    CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/vcard.py
    CalendarServer/branches/users/gaya/sharedgroups/txdav/carddav/datastore/sql.py

Modified: CalDAVTester/branches/users/gaya/sharedgroupstester/Resource/CardDAV/sharing/addressbooks/group/13.vcf
===================================================================
--- CalDAVTester/branches/users/gaya/sharedgroupstester/Resource/CardDAV/sharing/addressbooks/group/13.vcf	2012-12-02 19:46:20 UTC (rev 10116)
+++ CalDAVTester/branches/users/gaya/sharedgroupstester/Resource/CardDAV/sharing/addressbooks/group/13.vcf	2012-12-02 21:13:11 UTC (rev 10117)
@@ -6,6 +6,6 @@
 N:Sub Group;;;;
 REV:20120503T194243Z
 X-ADDRESSBOOKSERVER-KIND:group
+X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
 X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:FCBA0FA3-00B2-4C95-B4EC-4CCC4843F8B1:ABPerson
-X-ADDRESSBOOKSERVER-MEMBER:urn:uuid:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
 END:VCARD

Modified: CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/vcard.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/vcard.py	2012-12-02 19:46:20 UTC (rev 10116)
+++ CalendarServer/branches/users/gaya/sharedgroups/twistedcaldav/vcard.py	2012-12-02 21:13:11 UTC (rev 10117)
@@ -393,6 +393,13 @@
         self._pycard.removeProperty(property._pycard)
         self._pycard.finalise()
 
+    def removeProperties(self, name):
+        """
+        remove all properties with name
+        @param name: the name of the properties to remove.
+        """
+        self._pycard.removeProperties(name)
+
     def replaceProperty(self, property):
         """
         Add or replace a property in this component.

Modified: CalendarServer/branches/users/gaya/sharedgroups/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/gaya/sharedgroups/txdav/carddav/datastore/sql.py	2012-12-02 19:46:20 UTC (rev 10116)
+++ CalendarServer/branches/users/gaya/sharedgroups/txdav/carddav/datastore/sql.py	2012-12-02 21:13:11 UTC (rev 10117)
@@ -159,7 +159,16 @@
                     abo.CREATED,
                     abo.MODIFIED))
 
+    @classmethod
+    def _columnsWithResourceIDsQuery(cls, columns, resourceIDs): #@NoSelf
+        """
+        DAL statement to retrieve addressbook object rows with given columns.
+        """
+        obj = cls._objectSchema
+        return Select(columns, From=obj,
+                      Where=obj.RESOURCE_ID.In(Parameter("resourceIDs", len(resourceIDs))),)
 
+
 class AddressBook(CommonHomeChild, AddressBookSharingMixIn):
     """
     SQL-based implementation of L{IAddressBook}.
@@ -305,23 +314,16 @@
         returnValue(tuple(allMemberIDs))
 
 
-    @classmethod
-    def _objectResourceNamesWithResourceIDsQuery(cls, resourceIDs): #@NoSelf
-        """
-        DAL query to load all object resource names for a home child.
-        """
-        abo = schema.ADDRESSBOOK_OBJECT
-        return Select([abo.RESOURCE_NAME], From=abo,
-                      Where=abo.RESOURCE_ID.In(Parameter("resourceIDs", len(resourceIDs))),
-                      )
-
-
     @inlineCallbacks
     def listObjectResources(self):
         if self._objectNames is None:
+            abo = schema.ADDRESSBOOK_OBJECT
             memberIDs = yield self._allAddressBookObjectIDs()
-            rows = (yield self._objectResourceNamesWithResourceIDsQuery(memberIDs).on(
-                self._txn, resourceIDs=memberIDs)) if memberIDs else []
+            rows = (yield self._columnsWithResourceIDsQuery(
+                    [abo.RESOURCE_NAME],
+                    memberIDs).on(
+                        self._txn, resourceIDs=memberIDs)
+                    ) if memberIDs else []
             self._objectNames = sorted([row[0] for row in rows])
 
         returnValue(self._objectNames)
@@ -378,33 +380,53 @@
                 raise DeleteOfShadowGroupNotAllowedError
 
 
-        # delete members table row for this object
         aboMembers = schema.ABO_MEMBERS
-        groupIDRows = yield Select(
-            [aboMembers.GROUP_ID],
-             From=aboMembers,
-             Where=aboMembers.MEMBER_ID == self._resourceID,
-        ).on(self._txn)
+        aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
 
-        memberAddress = "urn:uuid:" + self._uid
         ownerGroup = yield self.ownerGroup()
-        removingObject = None if ownerGroup else self
-        for groupID in [groupIDRow[0] for groupIDRow in groupIDRows]:
-            if ownerGroup and groupID == self._ownerAddressBookResourceID:
-                pass  # convert delete in shared group to remove of membership only part 1
-            else:
-                groupObject = yield self._addressbook.objectResourceWithID(groupID)
-                groupComponent = yield groupObject.component()
-                assert memberAddress in groupComponent.resourceMemberAddresses(), "remove: member %s not in %s" % (self.component(), groupComponent)
-                groupComponent.removeProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
-                #groupComponent.replaceProperty(Property("PRODID", vCardProductID))
-                yield groupObject.updateDatabase(groupComponent, removingObject=removingObject)
+        if ownerGroup:
+            # convert delete in sharee shared group address book to remove of memberships
+            # that make this object visible to the sharee
 
-        if ownerGroup:
-            # convert delete in shared group to remove of member only part 2
+            # FIX ME: Combine into one query
+            memberIDs = yield self._addressbook._allAddressBookObjectIDs()
+
+            groupIDRows = yield Select(
+                [aboMembers.GROUP_ID],
+                From=aboMembers,
+                Where=(aboMembers.MEMBER_ID == self._resourceID).And(
+                        aboMembers.GROUP_ID != self._ownerAddressBookResourceID),
+            ).on(self._txn)
+            groupIDs = [groupIDRow[0] for groupIDRow in groupIDRows]
+
+            for groupID in set(groupIDs) & set(memberIDs):
+                yield Delete(
+                    aboMembers,
+                    Where=(aboMembers.MEMBER_ID == self._resourceID).And(
+                            aboMembers.GROUP_ID == groupID),
+                ).on(self._txn)
+
             ownerAddressBook = yield self.ownerAddressBook()
             yield self._changeAddressBookRevision(ownerAddressBook)
+
         else:
+            # delete members table rows for this object,...
+            groupIDRows = yield Delete(
+                aboMembers,
+                Where=aboMembers.MEMBER_ID == self._resourceID,
+                Return=aboMembers.GROUP_ID
+            ).on(self._txn)
+
+            # add to foreign member table row by UID
+            memberAddress = "urn:uuid:" + self._uid
+            for groupID in [groupIDRow[0] for groupIDRow in groupIDRows]:
+                if groupID != self._ownerAddressBookResourceID: # no aboForeignMembers on address books
+                    yield Insert(
+                            {aboForeignMembers.GROUP_ID: groupID,
+                             aboForeignMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
+                             aboForeignMembers.MEMBER_ADDRESS: memberAddress, }
+                        ).on(self._txn)
+
             yield super(AddressBookObject, self).remove()
             self._kind = None
             self._ownerAddressBookResourceID = None
@@ -453,22 +475,16 @@
         @return: L{self} if object exists in the DB, else C{None}
         """
 
+        memberIDs = yield self._addressbook._allAddressBookObjectIDs()
         if self._name:
-            memberIDs = yield self._addressbook._allAddressBookObjectIDs()
             rows = (yield self._allWithResourceIDAndName(memberIDs).on(
                 self._txn, name=self._name,
                 resourceIDs=memberIDs,)) if memberIDs else []
         elif self._uid:
-            memberIDs = yield self._addressbook._allAddressBookObjectIDs()
             rows = (yield self._allWithResourceIDAndUID(memberIDs).on(
                 self._txn, uid=self._uid,
                 resourceIDs=memberIDs,)) if memberIDs else []
         elif self._resourceID:
-            if (self._resourceID == self._addressbook._resourceID):
-                # Allow shadowGroup creation by resourceID only, even this is owned address book
-                memberIDs = [self._resourceID]
-            else:
-                memberIDs = yield self._addressbook._allAddressBookObjectIDs()
             rows = (yield self._allWithResourceID.on(
                 self._txn, resourceID=self._resourceID,)) if (self._resourceID in memberIDs) else []
 
@@ -515,19 +531,11 @@
          self._created,
          self._modified,) = tuple(row)
 
-
     @classmethod
-    def _allColumnsWithResourceIDsQuery(cls, resourceIDs): #@NoSelf
-        obj = cls._objectSchema
-        return Select(cls._allColumns, From=obj,
-                      Where=obj.RESOURCE_ID.In(Parameter("resourceIDs", len(resourceIDs))),)
-
-
-    @classmethod
     @inlineCallbacks
     def _allColumnsWithParent(cls, parent): #@NoSelf
         memberIDs = yield parent._allAddressBookObjectIDs()
-        rows = (yield cls._allColumnsWithResourceIDsQuery(memberIDs).on(
+        rows = (yield cls._columnsWithResourceIDsQuery(cls._allColumns, memberIDs).on(
             parent._txn, resourceIDs=memberIDs)) if memberIDs else []
         returnValue(rows)
 
@@ -568,9 +576,6 @@
 
         if self._addressbook.owned():
             # update revision table of the sharee group address book
-            #
-            # Alternatively, we could create an address book object for this group
-            #                and update that.
             if self._kind == _ABO_KIND_GROUP:  # optimization
                 for shareeAddressBook in (yield self.asShared()):
                     yield self._changeAddressBookRevision(shareeAddressBook, inserting)
@@ -578,8 +583,8 @@
                     # one is enough because all have the same resourceID
                     break
         else:
-            if self._addressbook._resourceID != self._ownerAddressBookResourceID: # this is a group shared address book
-                # update owner address book
+            if self._addressbook._resourceID != self._ownerAddressBookResourceID:
+                # update revisions table a shared group's containing address book
                 ownerAddressBook = yield self.ownerAddressBook()
                 yield self._changeAddressBookRevision(ownerAddressBook, inserting)
 
@@ -619,12 +624,6 @@
         returnValue(self._ownerAddressBook)
 
 
-    @inlineCallbacks
-    def shadowGroup(self):
-        ownerAddressBook = yield self.ownerAddressBook()
-        returnValue((yield ownerAddressBook.objectResourceWithID(ownerAddressBook._resourceID)))
-
-
     @classmethod
     def _resourceIDAndUIDForUIDsAndAddressBookResourceIDQuery(cls, uids): #@NoSelf
         abo = schema.ADDRESSBOOK_OBJECT
@@ -646,24 +645,6 @@
         @type component: L{Component}
         """
 
-        abo = schema.ADDRESSBOOK_OBJECT
-        aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
-        aboMembers = schema.ABO_MEMBERS
-
-        componentText = str(component)
-        self._objectText = componentText
-
-        # ADDRESSBOOK_OBJECT table update
-        uid = component.resourceUID()
-        assert inserting or self._uid == uid # can't change UID. Should be checked in upper layers
-        self._uid = uid
-        self._md5 = hashlib.md5(componentText).hexdigest()
-        self._size = len(componentText)
-
-        # Special - if migrating we need to preserve the original md5    
-        if self._txn._migrating and hasattr(component, "md5"):
-            self._md5 = component.md5
-
         componentResourceKindToAddressBookObjectKindMap = {
             "person": _ABO_KIND_PERSON,
             "group": _ABO_KIND_GROUP,
@@ -704,6 +685,42 @@
                     set(memberIDs) - set((yield self._addressbook._allAddressBookObjectIDs())):
                     raise GroupWithUnsharedAddressNotAllowedError
 
+            # don't store group members in object text
+
+            # sort addreses in component text
+            memberAddresses = component.resourceMemberAddresses()
+            component.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+            for memberAddress in sorted(memberAddresses):
+                component.addProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
+
+            # use sorted test to get size and md5
+            componentText = str(component)
+            self._md5 = hashlib.md5(componentText).hexdigest()
+            self._size = len(componentText)
+
+            # remove members from component get new text
+            component.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+            componentText = str(component)
+            self._objectText = componentText
+
+        else:
+            componentText = str(component)
+            self._md5 = hashlib.md5(componentText).hexdigest()
+            self._size = len(componentText)
+            self._objectText = componentText
+
+        uid = component.resourceUID()
+        assert inserting or self._uid == uid # can't change UID. Should be checked in upper layers
+        self._uid = uid
+
+        # Special - if migrating we need to preserve the original md5    
+        if self._txn._migrating and hasattr(component, "md5"):
+            self._md5 = component.md5
+
+        abo = schema.ADDRESSBOOK_OBJECT
+        aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
+        aboMembers = schema.ABO_MEMBERS
+
         if inserting:
 
             self._resourceID, self._created, self._modified = (
@@ -711,34 +728,31 @@
                     self._txn,
                     addressbookResourceID=self._ownerAddressBookResourceID,
                     name=self._name,
-                    text=componentText,
+                    text=self._objectText,
                     uid=self._uid,
                     md5=self._md5,
                     kind=self._kind,
                     ))[0]
 
-            # add this vCard to shared group and address book group
-            ownerGroup = yield self.ownerGroup()
-            groups = [ownerGroup] if ownerGroup else []
-            groups.append((yield self.shadowGroup()))
-
-            memberAddress = "urn:uuid:" + self._uid
-            for group in groups:
-                groupComponent = yield group.component()
-                assert memberAddress not in groupComponent.resourceMemberAddresses(), ("updateDatabase(): member %s already in %s" % (component, groupComponent))
-                groupComponent.addProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
-                #groupComponent.replaceProperty(Property("PRODID", vCardProductID))
-                yield group.updateDatabase(groupComponent)
-
             # delete foreign members table row for this object
             groupIDRows = yield Delete(
                 aboForeignMembers,
+                # should this be scoped to the owner address book?
                 Where=aboForeignMembers.MEMBER_ADDRESS == "urn:uuid:" + self._uid,
                 Return=aboForeignMembers.GROUP_ID
             ).on(self._txn)
+            groupIDs = [groupIDRow[0] for groupIDRow in groupIDRows]
 
-            # add to member table row by resourceID
-            for groupID in [groupIDRow[0] for groupIDRow in groupIDRows]:
+            # add group if of this owner address book
+            groupIDs.append(self._ownerAddressBookResourceID)
+
+            # add owner group if there is one
+            ownerGroup = yield self.ownerGroup()
+            if ownerGroup:
+                groupIDs.append(self._ownerGroup._resourceID)
+
+            # add to member table rows
+            for groupID in groupIDs:
                 yield Insert(
                     {aboMembers.GROUP_ID: groupID,
                      aboMembers.ADDRESSBOOK_ID: self._ownerAddressBookResourceID,
@@ -747,7 +761,7 @@
 
         else:
             self._modified = (yield Update(
-                {abo.VCARD_TEXT: componentText,
+                {abo.VCARD_TEXT: self._objectText,
                  abo.MD5: self._md5,
                  abo.MODIFIED: utcNowSQL},
                 Where=abo.RESOURCE_ID == self._resourceID,
@@ -841,6 +855,48 @@
         if fixed:
             self.log_error("Address data id=%s had fixable problems:\n  %s" % (self._resourceID, "\n  ".join(fixed),))
 
+        if self._kind == _ABO_KIND_GROUP:
+            assert not component.hasProperty("X-ADDRESSBOOKSERVER-MEMBER"), "database group vCard text contains members %s" % (component,)
+            # component.removeProperties("X-ADDRESSBOOKSERVER-MEMBER")
+
+            # generate member properties
+            # first get member resource ids
+            aboMembers = schema.ABO_MEMBERS
+            memberRows = yield Select([aboMembers.MEMBER_ID],
+                                 From=aboMembers,
+                                 Where=aboMembers.GROUP_ID == self._resourceID,).on(self._txn)
+            memberIDs = [memberRow[0] for memberRow in memberRows]
+
+            # then get member UIDs
+            abo = schema.ADDRESSBOOK_OBJECT
+            memberUIDRows = (yield self._columnsWithResourceIDsQuery(
+                             [abo.VCARD_UID],
+                             memberIDs).on(
+                                self._txn, resourceIDs=memberIDs)
+                            ) if memberIDs else []
+            memberUIDs = [memberUIDRow[0] for memberUIDRow in memberUIDRows]
+
+            # add prefix to get property string
+            memberAddresses = ["urn:uuid:" + memberUID for memberUID in memberUIDs]
+
+            # get foreign members
+            aboForeignMembers = schema.ABO_FOREIGN_MEMBERS
+            foreignMemberRows = yield Select([aboForeignMembers.MEMBER_ADDRESS],
+                                             From=aboForeignMembers,
+                                             Where=aboForeignMembers.GROUP_ID == self._resourceID,
+                                            ).on(self._txn)
+            foreignMembers = [foreignMemberRow[0] for foreignMemberRow in foreignMemberRows]
+
+
+            # now add the properties to the component
+            for memberAddress in sorted(memberAddresses + foreignMembers):
+                component.addProperty(Property("X-ADDRESSBOOKSERVER-MEMBER", memberAddress))
+
+            # FIXME, this should be set in the database
+            componentText = str(component)
+            self._md5 = hashlib.md5(componentText).hexdigest()
+            self._size = len(componentText)
+
         returnValue(component)
 
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20121202/2e845cce/attachment-0001.html>


More information about the calendarserver-changes mailing list