<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[11902] CalendarServer/branches/users/sagen/groupcacher</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/11902">11902</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2013-11-07 10:24:00 -0800 (Thu, 07 Nov 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Clean up the API a bit, and more testing</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhodelegatespy">CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhogroupspy">CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhotesttest_delegatespy">CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertwextwhotesttest_groupspy">CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py</a></li>
<li><a href="#CalendarServerbranchesuserssagengroupcachertxdavcommondatastoresqlpy">CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/delegates.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -34,19 +34,23 @@
</span><span class="cx">     if delegate.recordType == RecordType.group:
</span><span class="cx">         # find the groupID
</span><span class="cx">         groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
</span><del>-        yield txn.addDelegate(delegator.guid, groupID,
-            1 if readWrite else 0, True)
</del><ins>+        yield txn.addDelegateGroup(delegator.guid, groupID, readWrite)
</ins><span class="cx">     else:
</span><del>-        yield txn.addDelegate(delegator.guid, delegate.guid,
-            1 if readWrite else 0, False)
</del><ins>+        yield txn.addDelegate(delegator.guid, delegate.guid, readWrite)
</ins><span class="cx">         
</span><span class="cx"> 
</span><ins>+@inlineCallbacks
</ins><span class="cx"> def removeDelegate(txn, delegator, delegate, readWrite):
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Args are records
</span><span class="cx">     &quot;&quot;&quot;
</span><del>-    return txn.removeDelegate(delegator.guid, delegate.guid,
-        1 if readWrite else 0, delegate.recordType==RecordType.group)
</del><ins>+    if delegate.recordType == RecordType.group:
+        # find the groupID
+        groupID, name, membershipHash = (yield txn.groupByGUID(delegate.guid))
+        yield txn.removeDelegateGroup(delegator.guid, groupID, readWrite)
+    else:
+        yield txn.removeDelegate(delegator.guid, delegate.guid,
+            readWrite)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -56,32 +60,30 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     records = []
</span><span class="cx">     directory = delegator.service
</span><del>-    results = (yield txn.delegates(delegator.guid, 1 if readWrite else 0))
-    for row in results:
-        if row[0] != delegator.guid:
-            record = (yield directory.recordWithGUID(row[0]))
</del><ins>+    delegateGUIDs = (yield txn.delegates(delegator.guid, readWrite))
+    for guid in delegateGUIDs:
+        if guid != delegator.guid:
+            record = (yield directory.recordWithGUID(guid))
</ins><span class="cx">             if record is not None:
</span><span class="cx">                 records.append(record)
</span><span class="cx">     returnValue(records)
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> @inlineCallbacks
</span><del>-def delegateFor(txn, delegate, readWrite):
</del><ins>+def delegatedTo(txn, delegate, readWrite):
</ins><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     Args are records
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     records = []
</span><span class="cx">     directory = delegate.service
</span><del>-    results = (yield txn.delegators(delegate.guid, 1 if readWrite else 0))
-    for row in results:
-        if row[0] != delegate.guid:
-            record = (yield directory.recordWithGUID(row[0]))
</del><ins>+    delegatorGUIDs = (yield txn.delegators(delegate.guid, readWrite))
+    for guid in delegatorGUIDs:
+        if guid != delegate.guid:
+            record = (yield directory.recordWithGUID(guid))
</ins><span class="cx">             if record is not None:
</span><span class="cx">                 records.append(record)
</span><span class="cx">     returnValue(records)
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-@inlineCallbacks
</del><span class="cx"> def allGroupDelegates(txn):
</span><del>-    results = (yield txn.allGroupDelegates())
-    returnValue([r[0] for r in results])
</del><ins>+    return txn.allGroupDelegates()
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/groups.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue, succeed
</span><span class="cx"> from twext.enterprise.dal.syntax import Delete
</span><span class="cx"> from twext.who.idirectory import RecordType
</span><ins>+from twext.who.delegates import allGroupDelegates
</ins><span class="cx"> 
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> log = Logger()
</span><span class="lines">@@ -156,7 +157,9 @@
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def update(self, txn):
</span><del>-        # Pull in external proxy assignments and stick in proxy db
</del><ins>+        # Pull in external delegate assignments and stick in delegate db
+        # TODO
+
</ins><span class="cx">         # Figure out which groups matter
</span><span class="cx">         groupGUIDs = yield self.groupsToRefresh()
</span><span class="cx">         # For each of those groups, create a per-group refresh work item
</span><span class="lines">@@ -166,9 +169,7 @@
</span><span class="cx">             yield txn.enqueue(GroupRefreshWork,
</span><span class="cx">                 groupGUID=groupGUID, notBefore=notBefore)
</span><span class="cx"> 
</span><del>-        pass
</del><span class="cx"> 
</span><del>-
</del><span class="cx">     @inlineCallbacks
</span><span class="cx">     def refreshGroup(self, txn, groupGUID):
</span><span class="cx">         # Does the work of a per-group refresh work item
</span><span class="lines">@@ -253,21 +254,11 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><del>-    def groupsToRefresh(self):
-        delegatedGUIDs = set((yield self.proxyDB.getAllMembers()))
-        self.log.info(&quot;There are %d proxies&quot; % (len(delegatedGUIDs),))
-        self.log.info(&quot;Retrieving group hierarchy from directory&quot;)
</del><ins>+    def groupsToRefresh(self, txn):
+        delegatedGUIDs = set((yield allGroupDelegates(txn)))
+        self.log.info(&quot;There are %d group delegates&quot; % (len(delegatedGUIDs),))
</ins><span class="cx"> 
</span><del>-        # &quot;groups&quot; maps a group to its members; the keys and values consist
-        # of whatever directory attribute is used to refer to members.  The
-        # attribute value comes from record.cachedGroupsAlias().
-        # &quot;aliases&quot; maps the record.cachedGroupsAlias() value for a group
-        # back to the group's guid.
-        groups, aliases = (yield self.getGroups(guids=delegatedGUIDs))
-        groupGUIDs = set(aliases.keys())
-        self.log.info(&quot;%d groups retrieved from the directory&quot; %
-            (len(groupGUIDs),))
</del><ins>+        # TODO: Retrieve the set of attendee group guids
+        attendeeGroupGUIDs = set()
</ins><span class="cx"> 
</span><del>-        delegatedGUIDs = delegatedGUIDs.intersection(groupGUIDs)
-        self.log.info(&quot;%d groups are proxies&quot; % (len(delegatedGUIDs),))
-        returnValue(delegatedGUIDs)
</del><ins>+        returnValue(delegatedGUIDs.union(attendeeGroupGUIDs))
</ins></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_delegates.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -19,12 +19,13 @@
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx"> from twext.who.delegates import (
</span><del>-    addDelegate, removeDelegate, delegatesOf, delegateFor, allGroupDelegates
</del><ins>+    addDelegate, removeDelegate, delegatesOf, delegatedTo, allGroupDelegates
</ins><span class="cx"> )
</span><span class="cx"> from twext.who.groups import GroupCacher
</span><span class="cx"> from twext.who.test.test_xml import xmlService
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><ins>+from twext.who.idirectory import RecordType
</ins><span class="cx"> 
</span><span class="cx"> class DelegationTest(StoreTestCase):
</span><span class="cx"> 
</span><span class="lines">@@ -53,7 +54,7 @@
</span><span class="cx">         yield addDelegate(txn, delegator, delegate1, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals([&quot;sagen&quot;], [d.shortNames[0] for d in delegates])
</span><del>-        delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+        delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx">         self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</span><span class="cx"> 
</span><span class="cx">         # Add another delegate
</span><span class="lines">@@ -61,21 +62,21 @@
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals(set([&quot;sagen&quot;, &quot;cdaboo&quot;]),
</span><span class="cx">             set([d.shortNames[0] for d in delegates]))
</span><del>-        delegators = (yield delegateFor(txn, delegate2, True))
</del><ins>+        delegators = (yield delegatedTo(txn, delegate2, True))
</ins><span class="cx">         self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</span><span class="cx"> 
</span><span class="cx">         # Remove 1 delegate
</span><span class="cx">         yield removeDelegate(txn, delegator, delegate1, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals([&quot;cdaboo&quot;], [d.shortNames[0] for d in delegates])
</span><del>-        delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+        delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx">         self.assertEquals(0, len(delegators))
</span><span class="cx"> 
</span><span class="cx">         # Remove the other delegate
</span><span class="cx">         yield removeDelegate(txn, delegator, delegate2, True)
</span><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals(0, len(delegates))
</span><del>-        delegators = (yield delegateFor(txn, delegate2, True))
</del><ins>+        delegators = (yield delegatedTo(txn, delegate2, True))
</ins><span class="cx">         self.assertEquals(0, len(delegators))
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -85,8 +86,9 @@
</span><span class="cx">         txn = store.newTransaction()
</span><span class="cx"> 
</span><span class="cx">         delegator = yield self.xmlService.recordWithUID(&quot;__wsanchez__&quot;)
</span><ins>+        delegate1 = yield self.xmlService.recordWithUID(&quot;__sagen__&quot;)
</ins><span class="cx">         group1 = yield self.xmlService.recordWithUID(&quot;__top_group_1__&quot;)
</span><del>-        delegate1 = yield self.xmlService.recordWithUID(&quot;__sagen__&quot;)
</del><ins>+        group2 = yield self.xmlService.recordWithUID(&quot;__sub_group_1__&quot;)
</ins><span class="cx"> 
</span><span class="cx">         # Add group delegate, but before the group membership has been
</span><span class="cx">         # pulled in
</span><span class="lines">@@ -96,16 +98,16 @@
</span><span class="cx"> 
</span><span class="cx">         # Now refresh the group and there will be 3 delegates (contained
</span><span class="cx">         # within 2 nested groups)
</span><del>-        guid = &quot;49b350c69611477b94d95516b13856ab&quot;
-        yield self.groupCacher.refreshGroup(txn, guid)
</del><ins>+        # guid = &quot;49b350c69611477b94d95516b13856ab&quot;
+        yield self.groupCacher.refreshGroup(txn, group1.guid)
+        yield self.groupCacher.refreshGroup(txn, group2.guid)
</ins><span class="cx">         delegates = (yield delegatesOf(txn, delegator, True))
</span><span class="cx">         self.assertEquals(set([&quot;sagen&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;]),
</span><span class="cx">             set([d.shortNames[0] for d in delegates]))
</span><del>-        delegators = (yield delegateFor(txn, delegate1, True))
</del><ins>+        delegators = (yield delegatedTo(txn, delegate1, True))
</ins><span class="cx">         self.assertEquals([&quot;wsanchez&quot;], [d.shortNames[0] for d in delegators])
</span><span class="cx"> 
</span><span class="cx">         # Verify we can ask for all delegated-to groups
</span><del>-        group2 = yield self.xmlService.recordWithUID(&quot;__sub_group_1__&quot;)
</del><span class="cx">         yield addDelegate(txn, delegator, group2, True)
</span><span class="cx">         groups = (yield allGroupDelegates(txn))
</span><span class="cx">         self.assertEquals(
</span><span class="lines">@@ -114,8 +116,39 @@
</span><span class="cx">                 &quot;86144f73345a409782f1b782672087c7&quot;
</span><span class="cx">                 ]), set(groups))
</span><span class="cx"> 
</span><ins>+        # Delegate to a user who is already indirectly delegated-to 
+        yield addDelegate(txn, delegator, delegate1, True)
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(set([&quot;sagen&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;]),
+            set([d.shortNames[0] for d in delegates]))
</ins><span class="cx"> 
</span><ins>+        # Add a member to the group; they become a delegate
+        newSet = set()
+        for name in (&quot;wsanchez&quot;, &quot;cdaboo&quot;, &quot;sagen&quot;, &quot;glyph&quot;, &quot;dre&quot;):
+            record = (yield self.xmlService.recordWithShortName(RecordType.user,
+                name))
+            newSet.add(record.guid)
+        groupID, name, membershipHash = (yield txn.groupByGUID(group1.guid))
+        numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers(txn,
+            groupID, newSet))
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(set([&quot;sagen&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;, &quot;dre&quot;]),
+            set([d.shortNames[0] for d in delegates]))
</ins><span class="cx"> 
</span><ins>+        # Remove delegate access from the top group
+        yield removeDelegate(txn, delegator, group1, True)
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(set([&quot;sagen&quot;, &quot;cdaboo&quot;]),
+            set([d.shortNames[0] for d in delegates]))
+
+        # Remove delegate access from the sub group
+        yield removeDelegate(txn, delegator, group2, True)
+        delegates = (yield delegatesOf(txn, delegator, True))
+        self.assertEquals(set([&quot;sagen&quot;]),
+            set([d.shortNames[0] for d in delegates]))
+
+
+
</ins><span class="cx"> testXMLConfig = &quot;&quot;&quot;&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
</span><span class="cx"> 
</span><span class="cx"> &lt;directory realm=&quot;xyzzy&quot;&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertwextwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/twext/who/test/test_groups.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -75,15 +75,19 @@
</span><span class="cx">         self.assertEquals(membershipHash, &quot;e90052eb63d47f32d5b03df0073f7854&quot;)
</span><span class="cx"> 
</span><span class="cx">         results = (yield txn.membersOfGroup(groupID))
</span><del>-        for row in results:
-            print row[0]
</del><ins>+        self.assertEquals(
+            set([&quot;9064df911dbc4e079c2b6839b0953876&quot;,
+                 &quot;4ad155cbae9b475f986ce08a7537893e&quot;,
+                 &quot;3bdcb95484d54f6d8035eac19a6d6e1f&quot;,
+                 &quot;7d45cb10479e456bb54d528958c5734b&quot;]),
+            set([r[0] for r in results])
+        )
</ins><span class="cx"> 
</span><span class="cx">         records = (yield self.groupCacher.cachedMembers(txn, groupID))
</span><span class="cx">         self.assertEquals(
</span><span class="cx">             set([r.shortNames[0] for r in records]),
</span><span class="cx">             set([&quot;wsanchez&quot;, &quot;cdaboo&quot;, &quot;glyph&quot;, &quot;sagen&quot;])
</span><span class="cx">         )
</span><del>-        print records
</del><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserssagengroupcachertxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py (11901 => 11902)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py        2013-11-07 17:02:26 UTC (rev 11901)
+++ CalendarServer/branches/users/sagen/groupcacher/txdav/common/datastore/sql.py        2013-11-07 18:24:00 UTC (rev 11902)
</span><span class="lines">@@ -1016,6 +1016,7 @@
</span><span class="cx">                        de.READ_WRITE: Parameter(&quot;readWrite&quot;),
</span><span class="cx">                        })
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _addDelegateGroupQuery(cls): #@NoSelf
</span><span class="cx">         ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1024,6 +1025,7 @@
</span><span class="cx">                        ds.READ_WRITE: Parameter(&quot;readWrite&quot;),
</span><span class="cx">                        })
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _removeDelegateQuery(cls): #@NoSelf
</span><span class="cx">         de = schema.DELEGATES
</span><span class="lines">@@ -1033,6 +1035,7 @@
</span><span class="cx">                    de.READ_WRITE == Parameter(&quot;readWrite&quot;))
</span><span class="cx">             )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _removeDelegateGroupQuery(cls): #@NoSelf
</span><span class="cx">         ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1042,6 +1045,7 @@
</span><span class="cx">                    ds.READ_WRITE == Parameter(&quot;readWrite&quot;))
</span><span class="cx">             )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _selectDelegatesQuery(cls): #@NoSelf
</span><span class="cx">         de = schema.DELEGATES
</span><span class="lines">@@ -1050,6 +1054,7 @@
</span><span class="cx">                     de.READ_WRITE == Parameter(&quot;readWrite&quot;))
</span><span class="cx">                 )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _selectDelegateGroupsQuery(cls): #@NoSelf
</span><span class="cx">         ds = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1059,6 +1064,7 @@
</span><span class="cx">                     ds.READ_WRITE == Parameter(&quot;readWrite&quot;))
</span><span class="cx">                 )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _selectDirectDelegatorsQuery(cls): #@NoSelf
</span><span class="cx">         de = schema.DELEGATES
</span><span class="lines">@@ -1068,17 +1074,6 @@
</span><span class="cx">                     de.READ_WRITE == Parameter(&quot;readWrite&quot;))
</span><span class="cx">                 )
</span><span class="cx"> 
</span><del>-    &quot;&quot;&quot;
-    @classproperty
-    def _selectDelegatorsGroupsQuery(cls): #@NoSelf
-        ds = schema.DELEGATE_GROUPS
-        return Select([ds.DELEGATOR], From=ds,
-                Where=(
-                    ds.GROUP_ID == Parameter(&quot;groupID&quot;).And(
-                    ds.READ_WRITE == Parameter(&quot;readWrite&quot;))
-                )
-            )
-    &quot;&quot;&quot;
</del><span class="cx"> 
</span><span class="cx">     @classproperty
</span><span class="cx">     def _selectIndirectDelegatorsQuery(cls): #@NoSelf
</span><span class="lines">@@ -1101,6 +1096,7 @@
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><ins>+
</ins><span class="cx">     @classproperty
</span><span class="cx">     def _selectIndirectDelegatesQuery(cls): #@NoSelf
</span><span class="cx">         dg = schema.DELEGATE_GROUPS
</span><span class="lines">@@ -1121,70 +1117,166 @@
</span><span class="cx">             )
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-    def addDelegate(self, delegator, delegate, readWrite, isGroup):
-        if isGroup:
-            return self._addDelegateGroupQuery.on(self, delegator=delegator,
-                groupID=delegate, readWrite=readWrite)
-        else:
-            return self._addDelegateQuery.on(self, delegator=delegator,
-                delegate=delegate, readWrite=readWrite)
</del><span class="cx"> 
</span><del>-    def removeDelegate(self, delegator, delegate, readWrite, isGroup):
-        if isGroup:
-            return self._removeDelegateGroupQuery.on(self, delegator=delegator,
-                groupID=delegate, readWrite=readWrite)
-        else:
-            return self._removeDelegateQuery.on(self, delegator=delegator,
-                delegate=delegate, readWrite=readWrite)
</del><ins>+    def addDelegate(self, delegator, delegate, readWrite):
+        &quot;&quot;&quot;
+        Adds a row to the DELEGATES table.  The delegate should not be a
+        group.  To delegate to a group, call addDelegateGroup() instead.
</ins><span class="cx"> 
</span><del>-    &quot;&quot;&quot;
-    def directDelegates(self, delegator, readWrite):
-        return self._selectDelegatesQuery.on(self, delegator=delegator,
-            readWrite=readWrite)
</del><ins>+        @param delegator: the GUID of the delegator
+        @type delegator: C{str} in normalized form (lowercase, no dashes)
+        @param delegate: the GUID of the delegate
+        @type delegate: C{str} in normalized form (lowercase, no dashes)
+        @param readWrite: grant read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._addDelegateQuery.on(self, delegator=delegator,
+            delegate=delegate, readWrite=1 if readWrite else 0)
</ins><span class="cx"> 
</span><del>-    def groupDelegates(self, delegator, readWrite):
-        return self._selectDelegateGroupssQuery.on(self, delegator=delegator,
-            readWrite=readWrite)
-    &quot;&quot;&quot;
</del><span class="cx"> 
</span><ins>+    def addDelegateGroup(self, delegator, delegateGroupID, readWrite):
+        &quot;&quot;&quot;
+        Adds a row to the DELEGATE_GROUPS table.  The delegate should be a
+        group.  To delegate to a person, call addDelegate() instead.
+
+        @param delegator: the GUID of the delegator
+        @type delegator: C{str} in normalized form (lowercase, no dashes)
+        @param delegateGroupID: the GROUP_ID of the delegate group
+        @type delegateGroupID: C{int}
+        @param readWrite: grant read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._addDelegateGroupQuery.on(self, delegator=delegator,
+            groupID=delegateGroupID, readWrite=1 if readWrite else 0)
+
+
+    def removeDelegate(self, delegator, delegate, readWrite):
+        &quot;&quot;&quot;
+        Removes a row from the DELEGATES table.  The delegate should not be a
+        group.  To remove a delegate group, call removeDelegateGroup() instead.
+
+        @param delegator: the GUID of the delegator
+        @type delegator: C{str} in normalized form (lowercase, no dashes)
+        @param delegate: the GUID of the delegate
+        @type delegate: C{str} in normalized form (lowercase, no dashes)
+        @param readWrite: remove read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._removeDelegateQuery.on(self, delegator=delegator,
+            delegate=delegate, readWrite=1 if readWrite else 0)
+
+
+    def removeDelegateGroup(self, delegator, delegateGroupID, readWrite):
+        &quot;&quot;&quot;
+        Removes a row from the DELEGATE_GROUPS table.  The delegate should be a
+        group.  To remove a delegate person, call removeDelegate() instead.
+
+        @param delegator: the GUID of the delegator
+        @type delegator: C{str} in normalized form (lowercase, no dashes)
+        @param delegateGroupID: the GROUP_ID of the delegate group
+        @type delegateGroupID: C{int}
+        @param readWrite: remove read and write access if True, otherwise
+            read-only access
+        @type readWrite: C{boolean}
+        &quot;&quot;&quot;
+        return self._removeDelegateGroupQuery.on(self, delegator=delegator,
+            groupID=delegateGroupID, readWrite=1 if readWrite else 0)
+
+
</ins><span class="cx">     @inlineCallbacks
</span><span class="cx">     def delegates(self, delegator, readWrite):
</span><ins>+        &quot;&quot;&quot;
+        Returns the GUIDs of all delegates for the given delegator.  If
+        delegate access was granted to any groups, those groups' members
+        (flattened) will be included. No GUIDs of the groups themselves
+        will be returned.
</ins><span class="cx"> 
</span><ins>+        @param delegator: the GUID of the delegator
+        @type delegator: C{str} in normalized form (lowercase, no dashes)
+        @param readWrite: the access-type to check for; read and write
+            access if True, otherwise read-only access
+        @type readWrite: C{boolean}
+        @returns: the GUIDs of the delegates (for the specified access
+            type)
+        @rtype: a Deferred resulting in a set
+        &quot;&quot;&quot;
+        delegates = set()
+
</ins><span class="cx">         # First get the direct delegates
</span><span class="cx">         results = (yield self._selectDelegatesQuery.on(self,
</span><del>-            delegator=delegator, readWrite=readWrite))
</del><ins>+            delegator=delegator, readWrite=1 if readWrite else 0))
+        for row in results:
+            delegates.add(row[0])
</ins><span class="cx"> 
</span><span class="cx">         # Finally get those who are in groups which have been delegated to
</span><del>-        results.extend((yield self._selectIndirectDelegatesQuery.on(self,
-            delegator=delegator, readWrite=readWrite)))
</del><ins>+        results = (yield self._selectIndirectDelegatesQuery.on(self,
+            delegator=delegator, readWrite=1 if readWrite else 0))
+        for row in results:
+            delegates.add(row[0])
</ins><span class="cx"> 
</span><del>-        returnValue(results)
</del><ins>+        returnValue(delegates)
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="cx">     def delegators(self, delegate, readWrite):
</span><ins>+        &quot;&quot;&quot;
+        Returns the GUIDs of all delegators which have granted access to
+        the given delegate, either directly or indirectly via groups.
</ins><span class="cx"> 
</span><ins>+        @param delegate: the GUID of the delegate
+        @type delegate: C{str} in normalized form (lowercase, no dashes)
+        @param readWrite: the access-type to check for; read and write
+            access if True, otherwise read-only access
+        @type readWrite: C{boolean}
+        @returns: the GUIDs of the delegators (for the specified access
+            type)
+        @rtype: a Deferred resulting in a set
+        &quot;&quot;&quot;
+        delegators = set()
+
</ins><span class="cx">         # First get the direct delegators
</span><span class="cx">         results = (yield self._selectDirectDelegatorsQuery.on(self,
</span><del>-            delegate=delegate, readWrite=readWrite))
</del><ins>+            delegate=delegate, readWrite=1 if readWrite else 0))
+        for row in results:
+            delegators.add(row[0])
</ins><span class="cx"> 
</span><span class="cx">         # Finally get those who have delegated to groups the delegate
</span><span class="cx">         # is a member of
</span><del>-        results.extend((yield self._selectIndirectDelegatorsQuery.on(self,
-            delegate=delegate, readWrite=readWrite)))
-        returnValue(results)
</del><ins>+        results = (yield self._selectIndirectDelegatorsQuery.on(self,
+            delegate=delegate, readWrite=1 if readWrite else 0))
+        for row in results:
+            delegators.add(row[0])
</ins><span class="cx"> 
</span><ins>+        returnValue(delegators)
</ins><span class="cx"> 
</span><ins>+
+    @inlineCallbacks
</ins><span class="cx">     def allGroupDelegates(self):
</span><ins>+        &quot;&quot;&quot;
+        Return the GUIDs of all groups which have been delegated to.  Useful
+        for obtaining the set of groups which need to be synchronized from
+        the directory.
+
+        @returns: the GUIDs of all delegated-to groups
+        @rtype: a Deferred resulting in a set
+        &quot;&quot;&quot;
</ins><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         dg = schema.DELEGATE_GROUPS
</span><span class="cx"> 
</span><del>-        return Select(
</del><ins>+        results = (yield Select(
</ins><span class="cx">             [gr.GROUP_GUID],
</span><span class="cx">             From=gr,
</span><span class="cx">             Where=(gr.GROUP_ID.In(Select([dg.GROUP_ID], From=dg, Where=None)))
</span><del>-        ).on(self)
</del><ins>+        ).on(self))
+        delegates = set()
+        for row in results:
+            delegates.add(row[0])
</ins><span class="cx"> 
</span><ins>+        returnValue(delegates)
+
</ins><span class="cx">     # End of Delegates
</span><span class="cx"> 
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>