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

source_changes at macosforge.org source_changes at macosforge.org
Fri Dec 8 22:30:19 PST 2006


Revision: 784
          http://trac.macosforge.org/projects/calendarserver/changeset/784
Author:   wsanchez at apple.com
Date:     2006-12-08 22:30:19 -0800 (Fri, 08 Dec 2006)

Log Message:
-----------
Add AggregateDirectoryService

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/test/util.py

Added Paths:
-----------
    CalendarServer/trunk/twistedcaldav/directory/aggregate.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py

Added: CalendarServer/trunk/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/aggregate.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/directory/aggregate.py	2006-12-09 06:30:19 UTC (rev 784)
@@ -0,0 +1,111 @@
+##
+# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Wilfredo Sanchez, wsanchez at apple.com
+##
+
+"""
+Directory service implementation which aggregates multiple directory
+services.
+"""
+
+__all__ = [
+    "AggregateDirectoryService",
+    "DuplicateRecordTypeError",
+]
+
+from twistedcaldav.directory.idirectory import IDirectoryService
+from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord, DirectoryError
+from twistedcaldav.directory.directory import UnknownRecordTypeError
+
+import twistedcaldav.directory.test.util
+
+class AggregateDirectoryService(DirectoryService):
+    """
+    L{IDirectoryService} implementation which aggregates multiple directory services.
+    """
+    baseGUID = "06FB225F-39E7-4D34-B1D1-29925F5E619B"
+
+    def __init__(self, services):
+        DirectoryService.__init__(self)
+
+        realmName = None
+        recordTypes = {}
+
+        for service in services:
+            service = IDirectoryService(service)
+
+            if service.realmName != realmName:
+                assert realmName is None, (
+                    "Aggregated directory services must have the same realm name: %r != %r"
+                    % (service.realmName, realmName)
+                )
+                realmName = service.realmName
+
+            if not hasattr(service, "recordTypePrefix"):
+                service.recordTypePrefix = ""
+            prefix = service.recordTypePrefix
+
+            for recordType in (prefix + r for r in service.recordTypes()):
+                if recordType in recordTypes:
+                    raise DuplicateRecordTypeError(
+                        "%r is in multiple services: %s, %s"
+                        % (recordType, recordTypes[recordType], service)
+                    )
+                recordTypes[recordType] = service
+
+        self.realmName = realmName
+        self._recordTypes = recordTypes
+
+    def recordTypes(self):
+        return set(self._recordTypes)
+
+    def listRecords(self, recordType):
+        return self._query("listRecords", recordType)
+
+    def recordWithShortName(self, recordType, shortName):
+        return self._query("recordWithShortName", recordType, shortName)
+
+    def recordWithGUID(self, guid):
+        return self._queryAll("recordWithGUID", address)
+
+    def recordWithCalendarUserAddress(self, address):
+        return self._queryAll("recordWithCalendarUserAddress", address)
+
+    def serviceForRecordType(self, recordType):
+        try:
+            return self._recordTypes[recordType]
+        except KeyError:
+            raise UnknownRecordTypeError(recordType)
+
+    def _query(self, query, recordType, *args):
+        service = self.serviceForRecordType(recordType)
+        return getattr(service, query)(
+            recordType[len(service.recordTypePrefix):],
+            *[a[len(service.recordTypePrefix):] for a in args]
+        )
+
+    def _queryAll(self, query, *args):
+        for service in self._recordTypes.values():
+            record = getattr(service, query)(*args)
+            if record is not None:
+                return record
+        else:
+            return None
+
+class DuplicateRecordTypeError(DirectoryError):
+    """
+    Duplicate record type.
+    """

Added: CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py	                        (rev 0)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_aggregate.py	2006-12-09 06:30:19 UTC (rev 784)
@@ -0,0 +1,77 @@
+##
+# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Wilfredo Sanchez, wsanchez at apple.com
+##
+
+from itertools import chain
+
+from twistedcaldav.directory.apache import BasicDirectoryService
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
+from twistedcaldav.directory.aggregate import AggregateDirectoryService
+
+from twistedcaldav.directory.test.test_apache import digestRealm, basicUserFile, groupFile
+from twistedcaldav.directory.test.test_xmlfile import xmlFile
+
+import twistedcaldav.directory.test.util
+
+apache_prefix = "apache:"
+xml_prefix = "xml:"
+
+testServices = (
+    (apache_prefix, twistedcaldav.directory.test.test_apache.Apache  ),
+    (xml_prefix   , twistedcaldav.directory.test.test_xmlfile.XMLFile),
+)
+
+class AggregatedDirectories (twistedcaldav.directory.test.util.DirectoryTestCase):
+    def _recordTypes(self):
+        recordTypes = set()
+        for prefix, testClass in testServices:
+            for recordType in testClass.recordTypes:
+                recordTypes.add(prefix + recordType)
+        return recordTypes
+
+    def _records(key):
+        def get(self):
+            records = {}
+            for prefix, testClass in testServices:
+                for record, info in getattr(testClass, key).iteritems():
+                    info = dict(info)
+                    info["prefix"] = prefix
+                    info["members"] = tuple(
+                        (t, prefix + s) for t, s in info.get("members", {})
+                    )
+                    records[prefix + record] = info
+            return records
+        return get
+
+    recordTypes = property(_recordTypes)
+    users = property(_records("users"))
+    groups = property(_records("groups"))
+    resources = property(_records("resources"))
+
+    recordTypePrefixes = tuple(s[0] for s in testServices)
+
+    def service(self):
+        """
+        Returns an IDirectoryService.
+        """
+        apacheService = BasicDirectoryService(digestRealm, basicUserFile, groupFile)
+        apacheService.recordTypePrefix = apache_prefix
+
+        xmlService = XMLDirectoryService(xmlFile)
+        xmlService.recordTypePrefix = xml_prefix
+
+        return AggregateDirectoryService((apacheService, xmlService))

Modified: CalendarServer/trunk/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/util.py	2006-12-09 03:49:36 UTC (rev 783)
+++ CalendarServer/trunk/twistedcaldav/directory/test/util.py	2006-12-09 06:30:19 UTC (rev 784)
@@ -23,6 +23,8 @@
 from twisted.cred.credentials import UsernamePassword
 from twisted.web2.auth.digest import DigestedCredentials, calcResponse, calcHA1
 
+from twistedcaldav.directory.directory import UnknownRecordTypeError
+
 # FIXME: Add tests for GUID hooey, once we figure out what that means here
 
 class DirectoryTestCase (twisted.trial.unittest.TestCase):
@@ -48,6 +50,9 @@
         """
         raise NotImplementedError("Subclass needs to implement service()")
 
+    # For aggregator subclasses
+    recordTypePrefixes = ("",)
+
     def test_realm(self):
         """
         IDirectoryService.realm
@@ -90,44 +95,31 @@
 
         self.assertEquals(self.recordNames("resource"), set(self.resources.keys()))
 
-    def test_recordWithShortName_user(self):
+    def test_recordWithShortName(self):
         """
-        IDirectoryService.recordWithShortName("user")
+        IDirectoryService.recordWithShortName()
         """
-        if not self.users:
-            raise SkipTest("No users")
+        for recordType, data in (
+            ( "user"    , self.users     ),
+            ( "group"   , self.groups    ),
+            ( "resource", self.resources ),
+        ):
+            if not data:
+                raise SkipTest("No %s" % (recordType,))
 
-        service = self.service()
-        for shortName in self.users:
-            record = service.recordWithShortName("user", shortName)
-            self.compare(record, shortName, self.users[shortName])
-        self.assertEquals(service.recordWithShortName("user", "IDunnoWhoThisIsIReallyDont"), None)
+            service = self.service()
+            for shortName, info in data.iteritems():
+                record = service.recordWithShortName(info.get("prefix", "") + recordType, shortName)
+                self.failUnless(record, "No record (%s)%s" % (info.get("prefix", "") + recordType, shortName))
+                self.compare(record, shortName, data[shortName])
 
-    def test_recordWithShortName_group(self):
-        """
-        IDirectoryService.recordWithShortName("group")
-        """
-        if not self.groups:
-            raise SkipTest("No groups")
+            for prefix in self.recordTypePrefixes:
+                try:
+                    record = service.recordWithShortName(prefix + recordType, "IDunnoWhoThisIsIReallyDont")
+                except UnknownRecordTypeError:
+                    continue
+                self.assertEquals(record, None)
 
-        service = self.service()
-        for shortName in self.groups:
-            record = service.recordWithShortName("group", shortName)
-            self.compare(record, shortName, self.groups[shortName])
-        self.assertEquals(service.recordWithShortName("group", "IDunnoWhoThisIsIReallyDont"), None)
-
-    def test_recordWithShortName_resource(self):
-        """
-        XMLDirectoryService.recordWithShortName("resource")
-        """
-        if not self.resources:
-            raise SkipTest("No resources")
-
-        service = self.service()
-        for shortName in self.resources:
-            record = service.recordWithShortName("resource", shortName)
-            self.compare(record, shortName, self.resources[shortName])
-
     def test_recordWithGUID(self):
         service = self.service()
         record = None
@@ -161,9 +153,10 @@
             raise SkipTest("No groups")
 
         service = self.service()
-        for group in self.groups:
-            groupRecord = service.recordWithShortName("group", group)
-            result = set((m.recordType, m.shortName) for m in groupRecord.members())
+        for group, info in self.groups.iteritems():
+            prefix = info.get("prefix", "")
+            groupRecord = service.recordWithShortName(prefix + "group", group)
+            result = set((m.recordType, prefix + m.shortName) for m in groupRecord.members())
             expected = set(self.groups[group]["members"])
             self.assertEquals(
                 result, expected,
@@ -184,9 +177,10 @@
             ( "group", self.groups ),
         ):
             service = self.service()
-            for shortName in data:
-                record = service.recordWithShortName(recordType, shortName)
-                result = set(g.shortName for g in record.groups())
+            for shortName, info in data.iteritems():
+                prefix = info.get("prefix", "")
+                record = service.recordWithShortName(prefix + recordType, shortName)
+                result = set(prefix + g.shortName for g in record.groups())
                 expected = set(g for g in self.groups if (record.recordType, shortName) in self.groups[g]["members"])
                 self.assertEquals(
                     result, expected,
@@ -194,8 +188,18 @@
                 )
 
     def recordNames(self, recordType):
-        return set(r.shortName for r in self.service().listRecords(recordType))
+        service = self.service()
+        names = set()
+        for prefix in self.recordTypePrefixes:
+            try:
+                records = service.listRecords(prefix + recordType)
+            except UnknownRecordTypeError:
+                continue
+            for record in records:
+                names.add(prefix + record.shortName)
 
+        return names
+
     def allEntries(self):
         for data, recordType in (
             (self.users,     "user"    ),
@@ -219,7 +223,12 @@
         addresses = set(value("addresses"))
         addresses.add("urn:uuid:%s" % (record.guid,))
 
-        self.assertEquals(record.shortName, shortName)
+        if hasattr(record.service, "recordTypePrefix"):
+            prefix = record.service.recordTypePrefix
+        else:
+            prefix = ""
+
+        self.assertEquals(prefix + record.shortName, shortName)
         self.assertEquals(set(record.calendarUserAddresses), addresses)
 
         if value("guid"):
@@ -228,6 +237,13 @@
         if value("name"):
             self.assertEquals(record.fullName, value("name"))
 
+    def servicePrefix(self):
+        service = self.service()
+        if hasattr(service, "recordTypePrefix"):
+            return service.recordTypePrefix
+        else:
+            return ""
+
 class BasicTestCase (DirectoryTestCase):
     """
     Tests a directory implementation with basic auth.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061208/fb9ad9b0/attachment.html


More information about the calendarserver-changes mailing list