[CalendarServer-changes] [10617] CalendarServer/trunk/twext/who

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 1 09:46:16 PST 2013


Revision: 10617
          http://trac.calendarserver.org//changeset/10617
Author:   wsanchez at apple.com
Date:     2013-02-01 09:46:16 -0800 (Fri, 01 Feb 2013)
Log Message:
-----------
Add recordWith*() conveniance methods, implement in XML.

Modified Paths:
--------------
    CalendarServer/trunk/twext/who/directory.py
    CalendarServer/trunk/twext/who/idirectory.py
    CalendarServer/trunk/twext/who/test/test_directory.py
    CalendarServer/trunk/twext/who/test/test_xml.py
    CalendarServer/trunk/twext/who/xml.py

Modified: CalendarServer/trunk/twext/who/directory.py
===================================================================
--- CalendarServer/trunk/twext/who/directory.py	2013-02-01 16:30:18 UTC (rev 10616)
+++ CalendarServer/trunk/twext/who/directory.py	2013-02-01 17:46:16 UTC (rev 10617)
@@ -27,9 +27,11 @@
 
 from twisted.python.util import FancyEqMixin
 
+from twext.who.idirectory import DirectoryServiceError
 from twext.who.idirectory import QueryNotSupportedError
 from twext.who.idirectory import FieldName, RecordType
 from twext.who.idirectory import Operand
+from twext.who.idirectory import DirectoryQueryMatchExpression
 from twext.who.idirectory import IDirectoryService, IDirectoryRecord
 
 
@@ -65,29 +67,55 @@
 
 
     def recordsFromQuery(self, expressions, operand=Operand.AND):
-        results = None
+        expressionIterator = iter(expressions)
 
+        try:
+            expression = expressionIterator.next()
+        except StopIteration:
+            return set()
+
+        results = self.recordsFromExpression(expression)
+
         for expression in expressions:
+            if (operand == Operand.AND and not results):
+                # No need to bother continuing here
+                return set()
+
             recordsMatchingExpression = self.recordsFromExpression(expression)
 
-            if results is None:
-                results = recordsMatchingExpression
-                break
-
             if operand == Operand.AND:
-                raise NotImplementedError()
+                results &= recordsMatchingExpression
             elif operand == Operand.OR:
-                raise NotImplementedError()
+                results |= recordsMatchingExpression
             else:
                 raise QueryNotSupportedError("Unknown operand: %s" % (operand,))
 
-        if results is None:
-            return ()
-
         return results
 
 
+    def recordsWithFieldValue(self, fieldName, value):
+        return self.recordsFromExpression(DirectoryQueryMatchExpression(fieldName, value))
 
+    def recordWithUID(self, uid):
+        return uniqueResult(self.recordsWithFieldValue(FieldName.uid, uid))
+               
+    def recordWithGUID(self, guid):
+        return uniqueResult(self.recordsWithFieldValue(FieldName.guid, guid))
+
+    def recordsWithRecordType(self, recordType):
+        return self.recordsWithFieldValue(FieldName.recordType, recordType)
+
+    def recordWithShortName(self, recordType, shortName):
+        return uniqueResult(self.recordsFromQuery((
+            DirectoryQueryMatchExpression(FieldName.recordType, recordType),
+            DirectoryQueryMatchExpression(FieldName.shortNames, shortName ),
+        )))
+
+    def recordsWithEmailAddress(self, emailAddress):
+        return self.recordsWithFieldValue(FieldName.emailAddresses, emailAddress)
+               
+
+
 class DirectoryRecord(FancyEqMixin, object):
     implements(IDirectoryRecord)
 
@@ -150,3 +178,14 @@
             return self.fields[fieldName]
         except KeyError:
             raise AttributeError(name)
+
+
+
+def uniqueResult(values):
+    result = None
+    for value in values:
+        if result is None:
+            result = value
+        else:
+            raise DirectoryServiceError("Multiple values found where one expected.")
+    return result

Modified: CalendarServer/trunk/twext/who/idirectory.py
===================================================================
--- CalendarServer/trunk/twext/who/idirectory.py	2013-02-01 16:30:18 UTC (rev 10616)
+++ CalendarServer/trunk/twext/who/idirectory.py	2013-02-01 17:46:16 UTC (rev 10617)
@@ -167,13 +167,57 @@
 
     def recordsFromQuery(expressions, operand=Operand.AND):
         """
+        Find records matching a query consisting of an interable of
+        expressions and an L{Operand}.
         @return: a deferred iterable of matching L{IDirectoryRecord}s.
-        @raises: L{QueryNotSupportedError} is the query is not
+        @raises: L{QueryNotSupportedError} if the query is not
             supported by this directory service.
         """
 
+    def recordsWithFieldValue(fieldName, value):
+        """
+        Find records that have the given L{FieldName} with the given
+        value.
+        @return: an iterable of L{IDirectoryRecord}s.
+        """
 
+    def recordWithUID(uid):
+        """
+        Find the record that has the given L{FieldName.uid}.
+        @return: an iterable of L{IDirectoryRecord}s, or C{None} if
+            there is no such record.
+        """
+               
+    def recordWithGUID(guid):
+        """
+        Find the record that has the given L{FieldName.guid}.
+        @return: an iterable of L{IDirectoryRecord}s, or C{None} if
+            there is no such record.
+        """
 
+    def recordsWithRecordType(recordType):
+        """
+        Find the records that have the given L{RecordType}.
+        @return: an iterable of L{IDirectoryRecord}s.
+        """
+
+    def recordWithShortName(recordType, shortName):
+        """
+        Find the record that has the given L{RecordType} and
+        L{FieldName.shortName}.
+        @return: an iterable of L{IDirectoryRecord}s, or C{None} if
+            there is no such record.
+        """
+
+    def recordsWithEmailAddress(emailAddress):
+        """
+        Find the records that have the given L{FieldName.emailAddress}.
+        @return: an iterable of L{IDirectoryRecord}s, or C{None} if
+            there is no such record.
+        """
+
+
+
 class IDirectoryRecord(Interface):
     """
     Directory record.

Modified: CalendarServer/trunk/twext/who/test/test_directory.py
===================================================================
--- CalendarServer/trunk/twext/who/test/test_directory.py	2013-02-01 16:30:18 UTC (rev 10616)
+++ CalendarServer/trunk/twext/who/test/test_directory.py	2013-02-01 17:46:16 UTC (rev 10617)
@@ -21,6 +21,7 @@
 from zope.interface.verify import verifyObject, BrokenMethodImplementation
 
 from twisted.trial import unittest
+from twisted.trial.unittest import SkipTest
 
 from twext.who.idirectory import QueryNotSupportedError
 from twext.who.idirectory import RecordType, FieldName
@@ -32,6 +33,7 @@
 class BaseTest(unittest.TestCase):
     realmName = "xyzzy"
 
+
     def _testService(self):
         if not hasattr(self, "_service"):
             self._service = DirectoryService(self.realmName)
@@ -47,10 +49,12 @@
         except BrokenMethodImplementation, e:
             self.fail(e)
 
+
     def test_init(self):
         service = self._testService()
         self.assertEquals(service.realmName, self.realmName)
 
+
     def test_recordTypes(self):
         service = self._testService()
         self.assertEquals(
@@ -58,18 +62,36 @@
             set(service.RecordTypeClass.iterconstants())
         )
 
+
     def test_recordsFromQueryNone(self):
         service = self._testService()
         records = service.recordsFromQuery(())
         for record in records:
             self.failTest("No records expected")
 
+
     def test_recordsFromQueryBogus(self):
         service = self._testService()
         self.assertRaises(QueryNotSupportedError, service.recordsFromQuery, (object(),))
 
 
+    def test_recordWithUID(self):
+        raise SkipTest("Subclasses should implement this test.")
 
+    def test_recordWithGUID(self):
+        raise SkipTest("Subclasses should implement this test.")
+
+    def test_recordsWithRecordType(self):
+        raise SkipTest("Subclasses should implement this test.")
+
+    def test_recordWithShortName(self):
+        raise SkipTest("Subclasses should implement this test.")
+
+    def test_recordsWithEmailAddress(self):
+        raise SkipTest("Subclasses should implement this test.")
+
+
+
 class DirectoryRecordTest(BaseTest):
     fields_wsanchez = {
         FieldName.uid:            "wsanchez",
@@ -87,6 +109,7 @@
         FieldName.emailAddresses: ("glyph at calendarserver.org",)
     }
 
+
     def _testRecord(self, fields=None, service=None):
         if fields is None:
             fields = self.fields_wsanchez
@@ -94,6 +117,7 @@
             service = self._testService()
         return DirectoryRecord(service, fields)
 
+
     def test_interface(self):
         record = self._testRecord()
         try:
@@ -101,6 +125,7 @@
         except BrokenMethodImplementation, e:
             self.fail(e)
 
+
     def test_init(self):
         service  = self._testService()
         wsanchez = self._testRecord(self.fields_wsanchez)
@@ -108,6 +133,7 @@
         self.assertEquals(wsanchez.service, service)
         self.assertEquals(wsanchez.fields , self.fields_wsanchez)
 
+
     def test_initWithNoUID(self):
         fields = self.fields_wsanchez.copy()
         del fields[FieldName.uid]
@@ -117,6 +143,7 @@
         fields[FieldName.uid] = ""
         self.assertRaises(ValueError, self._testRecord, fields)
 
+
     def test_initWithNoRecordType(self):
         fields = self.fields_wsanchez.copy()
         del fields[FieldName.recordType]
@@ -126,6 +153,7 @@
         fields[FieldName.recordType] = ""
         self.assertRaises(ValueError, self._testRecord, fields)
 
+
     def test_initWithNoShortNames(self):
         fields = self.fields_wsanchez.copy()
         del fields[FieldName.shortNames]
@@ -143,11 +171,13 @@
         fields[FieldName.shortNames] = ("wsanchez", "")
         self.assertRaises(ValueError, self._testRecord, fields)
 
+
     def test_initWithBogusRecordType(self):
         fields = self.fields_wsanchez.copy()
         fields[FieldName.recordType] = object()
         self.assertRaises(ValueError, self._testRecord, fields)
 
+
     def test_compare(self):
         fields_glyphmod = self.fields_glyph.copy()
         del fields_glyphmod[FieldName.emailAddresses]
@@ -163,6 +193,7 @@
         self.assertNotEqual(glyphmod, wsanchez)
         self.assertNotEqual(wsanchez, wsanchezmod) # Different service
 
+
     def test_attributeAccess(self):
         wsanchez = self._testRecord(self.fields_wsanchez)
 

Modified: CalendarServer/trunk/twext/who/test/test_xml.py
===================================================================
--- CalendarServer/trunk/twext/who/test/test_xml.py	2013-02-01 16:30:18 UTC (rev 10616)
+++ CalendarServer/trunk/twext/who/test/test_xml.py	2013-02-01 17:46:16 UTC (rev 10617)
@@ -19,6 +19,8 @@
 """
 
 from twisted.python.filepath import FilePath
+
+from twext.who.idirectory import RecordType
 from twext.who.xml import DirectoryService
 
 from twext.who.test import test_directory
@@ -67,7 +69,37 @@
 
 
 class DirectoryServiceTest(BaseTest, test_directory.DirectoryServiceTest):
-    def test_XYZZY(self):
+    def test_recordWithUID(self):
         service = self._testService()
+        record = service.recordWithUID("wsanchez")
+        self.assertEquals(record.uid, "wsanchez")
 
-        service.loadRecords()
+
+    def test_recordWithGUID(self):
+        service = self._testService()
+        record = service.recordWithGUID("wsanchez")
+        self.assertEquals(record, None)
+
+
+    def test_recordsWithRecordType(self):
+        service = self._testService()
+        records = service.recordsWithRecordType(RecordType.user)
+        self.assertEquals(
+            set((record.uid for record in records)),
+            set(("wsanchez", "glyph")),
+        )
+
+
+    def test_recordWithShortName(self):
+        service = self._testService()
+        record = service.recordWithShortName(RecordType.user, "wsanchez")
+        self.assertEquals(record.uid, "wsanchez")
+
+
+    def test_recordsWithEmailAddress(self):
+        service = self._testService()
+        records = service.recordsWithEmailAddress("wsanchez at example.com")
+        self.assertEquals(
+            set((record.uid for record in records)),
+            set(("wsanchez",)),
+        )

Modified: CalendarServer/trunk/twext/who/xml.py
===================================================================
--- CalendarServer/trunk/twext/who/xml.py	2013-02-01 16:30:18 UTC (rev 10616)
+++ CalendarServer/trunk/twext/who/xml.py	2013-02-01 17:46:16 UTC (rev 10617)
@@ -32,6 +32,7 @@
 
 from twext.who.idirectory import DirectoryServiceError
 from twext.who.idirectory import RecordType, FieldName
+from twext.who.idirectory import MatchType
 from twext.who.idirectory import DirectoryQueryMatchExpression
 from twext.who.directory import DirectoryService as BaseDirectoryService
 from twext.who.directory import DirectoryRecord
@@ -240,8 +241,22 @@
         self._index = index
 
 
+    def indexedRecordsFromMatchExpression(self, expression):
+        if expression.matchType != MatchType.equals:
+            raise NotImplementedError("Handle MatchType != equals")
+
+        if expression.flags:
+            raise NotImplementedError("Handle QueryFlags")
+
+        return self.index[expression.fieldName].get(expression.fieldValue, ())
+
+
     def recordsFromExpression(self, expression):
         if isinstance(expression, DirectoryQueryMatchExpression):
-            raise NotImplementedError()
+            if expression.fieldName in self.indexedFields:
+                return self.indexedRecordsFromMatchExpression(expression)
+
+            raise NotImplementedError("Handle unindexed field")
+
         else:
             return BaseDirectoryService.recordsFromExpression(self, expression)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130201/5a3fcba5/attachment-0001.html>


More information about the calendarserver-changes mailing list