[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