[CalendarServer-changes] [4862] CalendarServer/trunk/twistedcaldav/directory

source_changes at macosforge.org source_changes at macosforge.org
Tue Dec 15 12:30:39 PST 2009


Revision: 4862
          http://trac.macosforge.org/projects/calendarserver/changeset/4862
Author:   sagen at apple.com
Date:     2009-12-15 12:30:36 -0800 (Tue, 15 Dec 2009)
Log Message:
-----------
Attendee lookup (principal property search) is much faster because only one OD round-trip is needed now

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2009-12-15 20:30:36 UTC (rev 4862)
@@ -340,22 +340,84 @@
 
     _fromODRecordTypes = dict([(b, a) for a, b in _toODRecordTypes.iteritems()])
 
+    def _uniqueTupleFromAttribute(self, attribute):
+        if attribute:
+            if isinstance(attribute, str):
+                return (attribute,)
+            else:
+                s = set()
+                return tuple([(s.add(x), x)[1] for x in attribute if x not in s])
+        else:
+            return ()
 
-    def recordsMatchingFields(self, fields, operand="or", recordType=None):
+    def _setFromAttribute(self, attribute, lower=False):
+        if attribute:
+            if isinstance(attribute, str):
+                return set((attribute.lower() if lower else attribute,))
+            else:
+                return set([item.lower() if lower else item for item in attribute])
+        else:
+            return ()
 
+    def recordsMatchingFields(self, fields, operand="or", recordType=None,
+        lookupMethod=opendirectory.queryRecordsWithAttributes_list):
+
         # Note that OD applies case-sensitivity globally across the entire
         # query, not per expression, so the current code uses whatever is
         # specified in the last field in the fields list
 
         def collectResults(results):
             self.log_info("Got back %d records from OD" % (len(results),))
-            for key, val in results:
-                self.log_debug("OD result: %s %s" % (key, val))
+            for key, value in results:
+                self.log_debug("OD result: %s %s" % (key, value))
                 try:
-                    guid = val[dsattributes.kDS1AttrGeneratedUID]
-                    record = self.recordWithGUID(guid)
-                    if record:
-                        yield record
+                    recordNodeName = value.get(
+                        dsattributes.kDSNAttrMetaNodeLocation)
+                    recordShortNames = self._uniqueTupleFromAttribute(
+                        value.get(dsattributes.kDSNAttrRecordName))
+                    if (recordNodeName == "/Local/Default" and not
+                        (config.Scheduling.iMIP.Username in recordShortNames)):
+                        continue
+
+                    recordGUID = value.get(dsattributes.kDS1AttrGeneratedUID)
+                    recordType = value.get(dsattributes.kDSNAttrRecordType)
+                    if isinstance(recordType, list):
+                        recordType = recordType[0]
+                    if not recordType:
+                        continue
+                    recordType = self._fromODRecordTypes[recordType]
+
+                    recordAuthIDs = self._setFromAttribute(
+                        value.get(dsattributes.kDSNAttrAltSecurityIdentities))
+                    recordFullName = value.get(
+                        dsattributes.kDS1AttrDistinguishedName)
+                    recordFirstName = value.get(dsattributes.kDS1AttrFirstName)
+                    recordLastName = value.get(dsattributes.kDS1AttrLastName)
+                    recordEmailAddresses = self._setFromAttribute(
+                        value.get(dsattributes.kDSNAttrEMailAddress),
+                        lower=True)
+
+                    # Create records but don't store them in our index or
+                    # send them to memcached, because these are transient,
+                    # existing only so we can create principal resource
+                    # objects that are used to generate the REPORT result.
+
+                    record = OpenDirectoryRecord(
+                        service               = self,
+                        recordType            = recordType,
+                        guid                  = recordGUID,
+                        nodeName              = recordNodeName,
+                        shortNames            = recordShortNames,
+                        authIDs               = recordAuthIDs,
+                        fullName              = recordFullName,
+                        firstName             = recordFirstName,
+                        lastName              = recordLastName,
+                        emailAddresses        = recordEmailAddresses,
+                        enabledForCalendaring = True,
+                        memberGUIDs           = (),
+                    )
+                    yield record
+
                 except KeyError:
                     pass
 
@@ -382,7 +444,7 @@
                     (recordTypes, operand, caseless, complexExpression))
 
                 results.extend(
-                    opendirectory.queryRecordsWithAttributes_list(
+                    lookupMethod(
                         directory,
                         complexExpression,
                         caseless,
@@ -410,7 +472,17 @@
             multiQuery,
             self.directory,
             queries,
-            [ dsattributes.kDS1AttrGeneratedUID ],
+            [
+                dsattributes.kDS1AttrGeneratedUID,
+                dsattributes.kDSNAttrRecordName,
+                dsattributes.kDSNAttrAltSecurityIdentities,
+                dsattributes.kDSNAttrRecordType,
+                dsattributes.kDS1AttrDistinguishedName,
+                dsattributes.kDS1AttrFirstName,
+                dsattributes.kDS1AttrLastName,
+                dsattributes.kDSNAttrEMailAddress,
+                dsattributes.kDSNAttrMetaNodeLocation,
+            ],
             operand
         )
         deferred.addCallback(collectResults)
@@ -509,25 +581,7 @@
                 self.log_error("OpenDirectory (node=%s) error: %s" % (self.realmName, str(ex)))
                 raise
 
-        def _uniqueTupleFromAttribute(attribute):
-            if attribute:
-                if isinstance(attribute, str):
-                    return (attribute,)
-                else:
-                    s = set()
-                    return tuple([(s.add(x), x)[1] for x in attribute if x not in s])
-            else:
-                return ()
 
-        def _setFromAttribute(attribute, lower=False):
-            if attribute:
-                if isinstance(attribute, str):
-                    return set((attribute.lower() if lower else attribute,))
-                else:
-                    return set([item.lower() if lower else item for item in attribute])
-            else:
-                return ()
-
         enabledRecords = []
         disabledRecords = []
 
@@ -535,15 +589,15 @@
 
             # Now get useful record info.
             recordGUID           = value.get(dsattributes.kDS1AttrGeneratedUID)
-            recordShortNames     = _uniqueTupleFromAttribute(value.get(dsattributes.kDSNAttrRecordName))
+            recordShortNames     = self._uniqueTupleFromAttribute(value.get(dsattributes.kDSNAttrRecordName))
             recordType           = value.get(dsattributes.kDSNAttrRecordType)
             if isinstance(recordType, list):
                 recordType = recordType[0]
-            recordAuthIDs        = _setFromAttribute(value.get(dsattributes.kDSNAttrAltSecurityIdentities))
+            recordAuthIDs        = self._setFromAttribute(value.get(dsattributes.kDSNAttrAltSecurityIdentities))
             recordFullName       = value.get(dsattributes.kDS1AttrDistinguishedName)
             recordFirstName      = value.get(dsattributes.kDS1AttrFirstName)
             recordLastName       = value.get(dsattributes.kDS1AttrLastName)
-            recordEmailAddresses = _setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
+            recordEmailAddresses = self._setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
             recordNodeName       = value.get(dsattributes.kDSNAttrMetaNodeLocation)
 
             if recordNodeName == "/Local/Default" and not (config.Scheduling.iMIP.Username in recordShortNames):

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-12-15 07:22:08 UTC (rev 4861)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-12-15 20:30:36 UTC (rev 4862)
@@ -333,7 +333,17 @@
         log.debug("No principal for calendar user address: %r" % (address,))
         return None
 
+    def principalForRecord(self, record):
+        if record is None:
+            return None
 
+        parent = self.getChild(uidsResourceName)
+        if record.enabledForCalendaring:
+            principal = DirectoryCalendarPrincipalResource(parent, record)
+        else:
+            principal = DirectoryPrincipalResource(parent, record)
+        return principal
+
     ##
     # Static
     ##

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2009-12-15 07:22:08 UTC (rev 4861)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py	2009-12-15 20:30:36 UTC (rev 4862)
@@ -21,6 +21,7 @@
 else:
     import twisted.web2.auth.digest
     import twistedcaldav.directory.test.util
+    from twisted.internet.defer import inlineCallbacks
     from twistedcaldav.directory.directory import DirectoryService
     from twistedcaldav.directory.appleopendirectory import OpenDirectoryRecord
     import dsattributes
@@ -240,3 +241,24 @@
 
             recordTypes = [DirectoryService.recordType_users, DirectoryService.recordType_groups, DirectoryService.recordType_locations, DirectoryService.recordType_resources]
             self.service().queryDirectory(recordTypes, self.service().INDEX_TYPE_CUA, "mailto:user1 at example.com", lookupMethod=lookupMethod)
+
+
+        @inlineCallbacks
+        def test_recordsMatchingFields(self):
+
+            def lookupMethod(obj, compound, casei, recordType, attributes, count=0):
+                if dsattributes.kDSStdRecordTypeUsers in recordType:
+                    return [
+                        ('morgen', {'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users', 'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/od.apple.com', 'dsAttrTypeStandard:RecordName': ['morgen', 'Morgen Sagen'], 'dsAttrTypeStandard:FirstName': 'Morgen', 'dsAttrTypeStandard:GeneratedUID': '83479230-821E-11DE-B6B0-DBB02C6D659D', 'dsAttrTypeStandard:LastName': 'Sagen', 'dsAttrTypeStandard:EMailAddress': 'morgen at example.com', 'dsAttrTypeStandard:RealName': 'Morgen Sagen'}),
+                        ('morehouse', {'dsAttrTypeStandard:RecordType': 'dsRecTypeStandard:Users', 'dsAttrTypeStandard:AppleMetaNodeLocation': '/LDAPv3/od.apple.com', 'dsAttrTypeStandard:RecordName': ['morehouse', 'Joe Morehouse'], 'dsAttrTypeStandard:FirstName': 'Joe', 'dsAttrTypeStandard:GeneratedUID': '98342930-90DC-11DE-A842-A29601FB13E8', 'dsAttrTypeStandard:LastName': 'Morehouse', 'dsAttrTypeStandard:EMailAddress': 'morehouse at example.com', 'dsAttrTypeStandard:RealName': 'Joe Morehouse'}),
+                    ]
+                else:
+                    return []
+
+            fields = [('fullName', 'mor', True, u'starts-with'), ('emailAddresses', 'mor', True, u'starts-with'), ('firstName', 'mor', True, u'starts-with'), ('lastName', 'mor', True, u'starts-with')]
+
+            results = (yield self.service().recordsMatchingFields(fields, lookupMethod=lookupMethod))
+            results = list(results)
+            self.assertEquals(len(results), 2)
+            for record in results:
+                self.assertTrue(isinstance(record, OpenDirectoryRecord))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20091215/54abf364/attachment-0001.html>


More information about the calendarserver-changes mailing list