<!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>[13683] CalendarServer/trunk</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/13683">13683</a></dd>
<dt>Author</dt> <dd>sagen@apple.com</dd>
<dt>Date</dt> <dd>2014-06-24 19:26:01 -0700 (Tue, 24 Jun 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Groups' disappearance from the directory affects their extant value</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarservertoolsprincipalspy">CalendarServer/trunk/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolstestdeprovisioncaldavdplist">CalendarServer/trunk/calendarserver/tools/test/deprovision/caldavd.plist</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolstestgatewaycaldavdplist">CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolstestprincipalscaldavdplist">CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist</a></li>
<li><a href="#CalendarServertrunkconfresourcescaldavdresourcesplist">CalendarServer/trunk/conf/resources/caldavd-resources.plist</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoresqlpy">CalendarServer/trunk/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresqlpy">CalendarServer/trunk/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavwhodelegatespy">CalendarServer/trunk/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServertrunktxdavwhogroupspy">CalendarServer/trunk/txdav/who/groups.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_delegatespy">CalendarServer/trunk/txdav/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_group_attendeespy">CalendarServer/trunk/txdav/who/test/test_group_attendees.py</a></li>
<li><a href="#CalendarServertrunktxdavwhotesttest_groupspy">CalendarServer/trunk/txdav/who/test/test_groups.py</a></li>
<li><a href="#CalendarServertrunktxdavwhoutilpy">CalendarServer/trunk/txdav/who/util.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/principals.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/principals.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/calendarserver/tools/principals.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -787,7 +787,9 @@
</span><span class="cx">     txn = store.newTransaction()
</span><span class="cx">     groupUIDs = yield txn.allGroupDelegates()
</span><span class="cx">     for groupUID in groupUIDs:
</span><del>-        groupID, name, _ignore_membershipHash, modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, modified, extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             groupUID
</span><span class="cx">         )
</span><span class="cx">         print(&quot;Group: \&quot;{name}\&quot; ({uid})&quot;.format(name=name, uid=groupUID))
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolstestdeprovisioncaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/test/deprovision/caldavd.plist (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/test/deprovision/caldavd.plist        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/calendarserver/tools/test/deprovision/caldavd.plist        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -115,12 +115,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span><span class="lines">@@ -135,7 +129,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -155,7 +149,7 @@
</span><span class="cx">       &lt;true/&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -167,14 +161,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Open Directory Service (Mac OS X) --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;DirectoryService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.appleopendirectory.OpenDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;node&lt;/key&gt;
</span><span class="lines">@@ -198,7 +192,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFiles&lt;/key&gt;
</span><span class="lines">@@ -207,14 +201,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Sqlite Augment Service --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;AugmentService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentSqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -229,7 +223,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -245,7 +239,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxySqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -259,7 +253,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -687,7 +681,7 @@
</span><span class="cx">     &lt;!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 --&gt;
</span><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><span class="cx">     &lt;false/&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- The retry-after value (in seconds) to return with a 503 error. --&gt;
</span><span class="cx">     &lt;key&gt;HTTPRetryAfter&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;180&lt;/integer&gt;
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolstestgatewaycaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/calendarserver/tools/test/gateway/caldavd.plist        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -127,12 +127,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolstestprincipalscaldavdplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -119,12 +119,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServertrunkconfresourcescaldavdresourcesplist"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/conf/resources/caldavd-resources.plist (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/conf/resources/caldavd-resources.plist        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/conf/resources/caldavd-resources.plist        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -99,12 +99,6 @@
</span><span class="cx">     &lt;key&gt;MaxAttendeesPerInstance&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;100&lt;/integer&gt;
</span><span class="cx"> 
</span><del>-    &lt;!-- Maximum number of instances allowed for a single RRULE --&gt;
-    &lt;!-- 0 for no limit --&gt;
-    &lt;key&gt;MaxInstancesForRRULE&lt;/key&gt;
-    &lt;integer&gt;400&lt;/integer&gt;
-
-
</del><span class="cx">     &lt;!--
</span><span class="cx">         Directory service
</span><span class="cx"> 
</span><span class="lines">@@ -119,7 +113,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -131,14 +125,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;key&gt;ResourceService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;Enabled&lt;/key&gt;
</span><span class="cx">       &lt;true/&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.xmlfile.XMLDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFile&lt;/key&gt;
</span><span class="lines">@@ -150,14 +144,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Open Directory Service (Mac OS X) --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;DirectoryService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.appleopendirectory.OpenDirectoryService&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;node&lt;/key&gt;
</span><span class="lines">@@ -181,7 +175,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentXMLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;xmlFiles&lt;/key&gt;
</span><span class="lines">@@ -190,14 +184,14 @@
</span><span class="cx">         &lt;/array&gt;
</span><span class="cx">       &lt;/dict&gt;
</span><span class="cx">     &lt;/dict&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- Sqlite Augment Service --&gt;
</span><span class="cx">     &lt;!--
</span><span class="cx">     &lt;key&gt;AugmentService&lt;/key&gt;
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentSqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -212,7 +206,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.augment.AugmentPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -228,7 +222,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxySqliteDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;dbpath&lt;/key&gt;
</span><span class="lines">@@ -242,7 +236,7 @@
</span><span class="cx">     &lt;dict&gt;
</span><span class="cx">       &lt;key&gt;type&lt;/key&gt;
</span><span class="cx">       &lt;string&gt;twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB&lt;/string&gt;
</span><del>-      
</del><ins>+
</ins><span class="cx">       &lt;key&gt;params&lt;/key&gt;
</span><span class="cx">       &lt;dict&gt;
</span><span class="cx">         &lt;key&gt;host&lt;/key&gt;
</span><span class="lines">@@ -672,7 +666,7 @@
</span><span class="cx">     &lt;!-- Support for Content-Encoding compression --&gt;
</span><span class="cx">     &lt;key&gt;ResponseCompression&lt;/key&gt;
</span><span class="cx">     &lt;false/&gt;
</span><del>-    
</del><ins>+
</ins><span class="cx">     &lt;!-- The retry-after value (in seconds) to return with a 503 error. --&gt;
</span><span class="cx">     &lt;key&gt;HTTPRetryAfter&lt;/key&gt;
</span><span class="cx">     &lt;integer&gt;180&lt;/integer&gt;
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -1996,7 +1996,10 @@
</span><span class="cx">                 groupUID = groupRecord.uid
</span><span class="cx">             else:
</span><span class="cx">                 groupUID = uidFromCalendarUserAddress(groupCUA)
</span><del>-            groupID, _ignore_name, membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupUID)
</del><ins>+            (
+                groupID, _ignore_name, membershipHash, _ignore_modDate,
+                _ignore_extant
+            ) = yield self._txn.groupByUID(groupUID)
</ins><span class="cx"> 
</span><span class="cx">             if groupID in groupIDToMembershipHashMap:
</span><span class="cx">                 if groupIDToMembershipHashMap[groupID] != membershipHash:
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -1033,8 +1033,8 @@
</span><span class="cx">             {
</span><span class="cx">                 gr.MEMBERSHIP_HASH: Parameter(&quot;membershipHash&quot;),
</span><span class="cx">                 gr.NAME: Parameter(&quot;name&quot;),
</span><del>-                gr.MODIFIED:
-                Parameter(&quot;timestamp&quot;)
</del><ins>+                gr.MODIFIED: Parameter(&quot;timestamp&quot;),
+                gr.EXTANT: Parameter(&quot;extant&quot;),
</ins><span class="cx">             },
</span><span class="cx">             Where=(gr.GROUP_UID == Parameter(&quot;groupUID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1044,7 +1044,7 @@
</span><span class="cx">     def _groupByUID(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         return Select(
</span><del>-            [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH, gr.MODIFIED],
</del><ins>+            [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH, gr.MODIFIED, gr.EXTANT],
</ins><span class="cx">             From=gr,
</span><span class="cx">             Where=(gr.GROUP_UID == Parameter(&quot;groupUID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1054,7 +1054,7 @@
</span><span class="cx">     def _groupByID(cls):
</span><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         return Select(
</span><del>-            [gr.GROUP_UID, gr.NAME, gr.MEMBERSHIP_HASH],
</del><ins>+            [gr.GROUP_UID, gr.NAME, gr.MEMBERSHIP_HASH, gr.EXTANT],
</ins><span class="cx">             From=gr,
</span><span class="cx">             Where=(gr.GROUP_ID == Parameter(&quot;groupID&quot;))
</span><span class="cx">         )
</span><span class="lines">@@ -1083,11 +1083,12 @@
</span><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-    def updateGroup(self, groupUID, name, membershipHash):
</del><ins>+    def updateGroup(self, groupUID, name, membershipHash, extant=True):
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         @type groupUID: C{unicode}
</span><span class="cx">         @type name: C{unicode}
</span><span class="cx">         @type membershipHash: C{str}
</span><ins>+        @type extant: C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         timestamp = datetime.datetime.utcnow()
</span><span class="cx">         return self._updateGroupQuery.on(
</span><span class="lines">@@ -1095,7 +1096,8 @@
</span><span class="cx">             name=name.encode(&quot;utf-8&quot;),
</span><span class="cx">             groupUID=groupUID.encode(&quot;utf-8&quot;),
</span><span class="cx">             timestamp=timestamp,
</span><del>-            membershipHash=membershipHash
</del><ins>+            membershipHash=membershipHash,
+            extant=(1 if extant else 0)
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -1107,7 +1109,8 @@
</span><span class="cx">         @type groupUID: C{unicode}
</span><span class="cx"> 
</span><span class="cx">         @return: Deferred firing with tuple of group ID C{str}, group name
</span><del>-            C{unicode}, membership hash C{str}, and modified timestamp
</del><ins>+            C{unicode}, membership hash C{str}, modified timestamp, and
+            extant C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         results = (
</span><span class="cx">             yield self._groupByUID.on(
</span><span class="lines">@@ -1120,6 +1123,7 @@
</span><span class="cx">                 results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                 results[0][2],  # membership hash
</span><span class="cx">                 results[0][3],  # modified timestamp
</span><ins>+                bool(results[0][4]),  # extant
</ins><span class="cx">             ))
</span><span class="cx">         elif create:
</span><span class="cx">             savepoint = SavepointAction(&quot;groupByUID&quot;)
</span><span class="lines">@@ -1139,6 +1143,7 @@
</span><span class="cx">                         results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                         results[0][2],  # membership hash
</span><span class="cx">                         results[0][3],  # modified timestamp
</span><ins>+                        bool(results[0][4]),  # extant
</ins><span class="cx">                     ))
</span><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="lines">@@ -1155,11 +1160,12 @@
</span><span class="cx">                         results[0][1].decode(&quot;utf-8&quot;),  # name
</span><span class="cx">                         results[0][2],  # membership hash
</span><span class="cx">                         results[0][3],  # modified timestamp
</span><ins>+                        bool(results[0][4]),  # extant
</ins><span class="cx">                     ))
</span><span class="cx">                 else:
</span><span class="cx">                     raise
</span><span class="cx">         else:
</span><del>-            returnValue((None, None, None, None))
</del><ins>+            returnValue((None, None, None, None, None))
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     @inlineCallbacks
</span><span class="lines">@@ -1169,7 +1175,7 @@
</span><span class="cx"> 
</span><span class="cx">         @type groupID: C{str}
</span><span class="cx">         @return: Deferred firing with a tuple of group UID C{unicode},
</span><del>-            group name C{unicode}, and membership hash C{str}
</del><ins>+            group name C{unicode}, membership hash C{str}, and extant C{boolean}
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         try:
</span><span class="cx">             results = (yield self._groupByID.on(self, groupID=groupID))[0]
</span><span class="lines">@@ -1177,7 +1183,8 @@
</span><span class="cx">                 results = (
</span><span class="cx">                     results[0].decode(&quot;utf-8&quot;),
</span><span class="cx">                     results[1].decode(&quot;utf-8&quot;),
</span><del>-                    results[2]
</del><ins>+                    results[2],
+                    bool(results[3])
</ins><span class="cx">                 )
</span><span class="cx">             returnValue(results)
</span><span class="cx">         except IndexError:
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/delegates.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/delegates.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/delegates.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -236,7 +236,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     if delegate.recordType == BaseRecordType.group:
</span><span class="cx">         # find the groupID
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             delegate.uid
</span><span class="cx">         )
</span><span class="cx">         yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
</span><span class="lines">@@ -260,7 +263,10 @@
</span><span class="cx">     &quot;&quot;&quot;
</span><span class="cx">     if delegate.recordType == BaseRecordType.group:
</span><span class="cx">         # find the groupID
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(
</ins><span class="cx">             delegate.uid
</span><span class="cx">         )
</span><span class="cx">         yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/groups.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/groups.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/groups.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -157,12 +157,14 @@
</span><span class="cx">             if (component.masterComponent() is None or not component.isRecurring()):
</span><span class="cx"> 
</span><span class="cx">                 # skip non-recurring old events, no instances
</span><del>-                if (yield calendarObject.removeOldEventGroupLink(
-                    component,
-                    instances=None,
-                    inserting=False,
-                    txn=self.transaction
-                )):
</del><ins>+                if (
+                    yield calendarObject.removeOldEventGroupLink(
+                        component,
+                        instances=None,
+                        inserting=False,
+                        txn=self.transaction
+                    )
+                ):
</ins><span class="cx">                     returnValue(None)
</span><span class="cx">             else:
</span><span class="cx">                 # skip recurring old events
</span><span class="lines">@@ -180,12 +182,14 @@
</span><span class="cx">                     lowerLimit=truncateLowerLimit,
</span><span class="cx">                     ignoreInvalidInstances=True
</span><span class="cx">                 )
</span><del>-                if (yield calendarObject.removeOldEventGroupLink(
-                    component,
-                    instances=instances,
-                    inserting=False,
-                    txn=self.transaction
-                )):
</del><ins>+                if (
+                    yield calendarObject.removeOldEventGroupLink(
+                        component,
+                        instances=instances,
+                        inserting=False,
+                        txn=self.transaction
+                    )
+                ):
</ins><span class="cx">                     returnValue(None)
</span><span class="cx"> 
</span><span class="cx">                 # split spanning events and only update present-future split result
</span><span class="lines">@@ -310,11 +314,17 @@
</span><span class="cx">             ) in changed:
</span><span class="cx">                 readDelegateGroupID = writeDelegateGroupID = None
</span><span class="cx">                 if readDelegateUID:
</span><del>-                    readDelegateGroupID, _ignore_name, hash, _ignore_modified = (
</del><ins>+                    (
+                        readDelegateGroupID, _ignore_name, hash,
+                        _ignore_modified, _ignore_extant
+                    ) = (
</ins><span class="cx">                         yield txn.groupByUID(readDelegateUID)
</span><span class="cx">                     )
</span><span class="cx">                 if writeDelegateUID:
</span><del>-                    writeDelegateGroupID, _ignore_name, hash, _ignore_modified = (
</del><ins>+                    (
+                        writeDelegateGroupID, _ignore_name, hash,
+                        _ignore_modified, _ignore_extant
+                    ) = (
</ins><span class="cx">                         yield txn.groupByUID(writeDelegateUID)
</span><span class="cx">                     )
</span><span class="cx">                 yield txn.assignExternalDelegates(
</span><span class="lines">@@ -344,20 +354,24 @@
</span><span class="cx">         else:
</span><span class="cx">             self.log.debug(&quot;Got group record: {u}&quot;, u=record.uid)
</span><span class="cx"> 
</span><del>-        groupID, cachedName, cachedMembershipHash, _ignore_modified = (
-            yield txn.groupByUID(
-                groupUID,
-                create=(record is not None)
-            )
</del><ins>+        (
+            groupID, cachedName, cachedMembershipHash, _ignore_modified,
+            extant
+        ) = yield txn.groupByUID(
+            groupUID,
+            create=(record is not None)
</ins><span class="cx">         )
</span><ins>+
</ins><span class="cx">         wps = tuple()
</span><span class="cx">         if groupID:
</span><span class="cx">             if record is not None:
</span><span class="cx">                 members = yield record.expandedMembers()
</span><del>-                name = record.fullNames[0]
</del><ins>+                name = record.displayName
+                extant = True
</ins><span class="cx">             else:
</span><span class="cx">                 members = frozenset()
</span><span class="cx">                 name = cachedName
</span><ins>+                extant = False
</ins><span class="cx"> 
</span><span class="cx">             membershipHashContent = hashlib.md5()
</span><span class="cx">             members = list(members)
</span><span class="lines">@@ -376,7 +390,9 @@
</span><span class="cx"> 
</span><span class="cx">             if membershipChanged or record is not None:
</span><span class="cx">                 # also updates group mod date
</span><del>-                yield txn.updateGroup(groupUID, name, membershipHash)
</del><ins>+                yield txn.updateGroup(
+                    groupUID, name, membershipHash, extant=extant
+                )
</ins><span class="cx"> 
</span><span class="cx">             if membershipChanged:
</span><span class="cx">                 newMemberUIDs = set()
</span><span class="lines">@@ -461,7 +477,7 @@
</span><span class="cx">             &quot;There are {count} group delegates&quot;, count=len(delegatedUIDs)
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        # Get groupUIDs for aoo group attendees
</del><ins>+        # Get groupUIDs for all group attendees
</ins><span class="cx">         groupAttendee = schema.GROUP_ATTENDEE
</span><span class="cx">         gr = schema.GROUPS
</span><span class="cx">         rows = yield Select(
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_delegates.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_delegates.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/test/test_delegates.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -207,7 +207,10 @@
</span><span class="cx">                 yield self.directory.recordWithShortName(RecordType.user, name)
</span><span class="cx">             )
</span><span class="cx">             newSet.add(record.uid)
</span><del>-        groupID, name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(group1.uid))
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = (yield txn.groupByUID(group1.uid))
</ins><span class="cx">         _ignore_numAdded, _ignore_numRemoved = (
</span><span class="cx">             yield self.groupCacher.synchronizeMembers(txn, groupID, newSet)
</span><span class="cx">         )
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_group_attendeespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -868,7 +868,10 @@
</span><span class="cx">         #finally, simulate an event that has become old
</span><span class="cx">         self.patch(CalendarDirectoryRecordMixin, &quot;expandedMembers&quot;, unpatchedExpandedMembers)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
+            _ignore_extant
+        ) = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</ins><span class="cx">         ga = schema.GROUP_ATTENDEE
</span><span class="cx">         yield Insert({
</span><span class="cx">                 ga.RESOURCE_ID: cobj._resourceID,
</span><span class="lines">@@ -1029,7 +1032,10 @@
</span><span class="cx">         #finally, simulate an event that has become old
</span><span class="cx">         self.patch(CalendarDirectoryRecordMixin, &quot;expandedMembers&quot;, unpatchedExpandedMembers)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
+            _ignore_extant
+        ) = yield self.transactionUnderTest().groupByUID(&quot;20000000-0000-0000-0000-000000000001&quot;)
</ins><span class="cx">         ga = schema.GROUP_ATTENDEE
</span><span class="cx">         yield Insert({
</span><span class="cx">                 ga.RESOURCE_ID: cobj._resourceID,
</span></span></pre></div>
<a id="CalendarServertrunktxdavwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/test/test_groups.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/test/test_groups.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/test/test_groups.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -18,11 +18,12 @@
</span><span class="cx"> Group membership caching implementation tests
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><del>-from txdav.who.groups import GroupCacher, diffAssignments
</del><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks
</span><span class="cx"> from twistedcaldav.test.util import StoreTestCase
</span><span class="cx"> from txdav.common.icommondatastore import NotFoundError
</span><ins>+from txdav.who.groups import GroupCacher, diffAssignments
+from txdav.who.test.support import TestRecord, CalendarInMemoryDirectoryService
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="lines">@@ -44,8 +45,8 @@
</span><span class="cx">         txn = store.newTransaction()
</span><span class="cx"> 
</span><span class="cx">         record = yield self.directory.recordWithUID(u&quot;__top_group_1__&quot;)
</span><del>-        _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
-        _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
</del><ins>+        yield txn.groupByUID(record.uid)
+        yield txn.groupByUID(record.uid)
</ins><span class="cx"> 
</span><span class="cx">         yield txn.commit()
</span><span class="cx"> 
</span><span class="lines">@@ -63,14 +64,19 @@
</span><span class="cx">         record = yield self.directory.recordWithUID(u&quot;__top_group_1__&quot;)
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, record.uid)
</span><span class="cx"> 
</span><del>-        groupID, _ignore_name, membershipHash, _ignore_modified = (yield txn.groupByUID(record.uid))
</del><ins>+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(record.uid))
</ins><span class="cx"> 
</span><ins>+        self.assertEquals(extant, True)
</ins><span class="cx">         self.assertEquals(membershipHash, &quot;553eb54e3bbb26582198ee04541dbee4&quot;)
</span><span class="cx"> 
</span><del>-        groupUID, name, membershipHash = (yield txn.groupByID(groupID))
</del><ins>+        groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID))
</ins><span class="cx">         self.assertEquals(groupUID, record.uid)
</span><span class="cx">         self.assertEquals(name, u&quot;Top Group 1&quot;)
</span><span class="cx">         self.assertEquals(membershipHash, &quot;553eb54e3bbb26582198ee04541dbee4&quot;)
</span><ins>+        self.assertEquals(extant, True)
</ins><span class="cx"> 
</span><span class="cx">         members = (yield txn.membersOfGroup(groupID))
</span><span class="cx">         self.assertEquals(
</span><span class="lines">@@ -107,7 +113,10 @@
</span><span class="cx">         # Refresh the group so it's assigned a group_id
</span><span class="cx">         uid = u&quot;__top_group_1__&quot;
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, uid)
</span><del>-        groupID, name, _ignore_membershipHash, _ignore_modified = (yield txn.groupByUID(uid))
</del><ins>+        (
+            groupID, name, _ignore_membershipHash, _ignore_modified,
+            _ignore_extant
+        ) = yield txn.groupByUID(uid)
</ins><span class="cx"> 
</span><span class="cx">         # Remove two members, and add one member
</span><span class="cx">         newSet = set()
</span><span class="lines">@@ -156,9 +165,12 @@
</span><span class="cx">         uid = u&quot;__top_group_1__&quot;
</span><span class="cx">         hash = &quot;553eb54e3bbb26582198ee04541dbee4&quot;
</span><span class="cx">         yield self.groupCacher.refreshGroup(txn, uid)
</span><del>-        groupID, _ignore_name, _ignore_membershipHash, _ignore_modified = yield txn.groupByUID(uid)
-        results = (yield txn.groupByID(groupID))
-        self.assertEquals((uid, u&quot;Top Group 1&quot;, hash), results)
</del><ins>+        (
+            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
+            extant
+        ) = yield txn.groupByUID(uid)
+        results = yield txn.groupByID(groupID)
+        self.assertEquals((uid, u&quot;Top Group 1&quot;, hash, True), results)
</ins><span class="cx"> 
</span><span class="cx">         yield txn.commit()
</span><span class="cx"> 
</span><span class="lines">@@ -414,3 +426,125 @@
</span><span class="cx">                 {&quot;D&quot;: (&quot;7&quot;, &quot;8&quot;), &quot;C&quot;: (&quot;4&quot;, &quot;5&quot;), &quot;A&quot;: (&quot;1&quot;, &quot;2&quot;)},
</span><span class="cx">             )
</span><span class="cx">         )
</span><ins>+
+
+class DynamicGroupTest(StoreTestCase):
+
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(DynamicGroupTest, self).setUp()
+
+        self.directory = CalendarInMemoryDirectoryService(None)
+        self.store.setDirectoryService(self.directory)
+        self.groupCacher = GroupCacher(self.directory)
+
+        self.numUsers = 100
+
+        # Add users
+        records = []
+        fieldName = self.directory.fieldName
+        for i in xrange(self.numUsers):
+            records.append(
+                TestRecord(
+                    self.directory,
+                    {
+                        fieldName.uid: u&quot;foo{ctr:05d}&quot;.format(ctr=i),
+                        fieldName.shortNames: (u&quot;foo{ctr:05d}&quot;.format(ctr=i),),
+                        fieldName.fullNames: (u&quot;foo{ctr:05d}&quot;.format(ctr=i),),
+                        fieldName.recordType: RecordType.user,
+                    }
+                )
+            )
+
+        # Add a group
+        records.append(
+            TestRecord(
+                self.directory,
+                {
+                    fieldName.uid: u&quot;testgroup&quot;,
+                    fieldName.recordType: RecordType.group,
+                }
+            )
+        )
+
+        yield self.directory.updateRecords(records, create=True)
+
+        group = yield self.directory.recordWithUID(u&quot;testgroup&quot;)
+        members = yield self.directory.recordsWithRecordType(RecordType.user)
+        yield group.setMembers(members)
+
+
+    @inlineCallbacks
+    def test_extant(self):
+        &quot;&quot;&quot;
+        Verify that once a group is removed from the directory, the next call
+        to refreshGroup() will set the &quot;extent&quot; to False.  Add the group back
+        to the directory and &quot;extent&quot; becomes True.
+        &quot;&quot;&quot;
+        store = self.storeUnderTest()
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        self.assertTrue(extant)
+
+        # Remove the group
+        yield self.directory.removeRecords([u&quot;testgroup&quot;])
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        # Extant = False
+        self.assertFalse(extant)
+
+        # The list of members stored in the DB for this group is now empty
+        txn = store.newTransaction()
+        members = yield txn.membersOfGroup(groupID)
+        yield txn.commit()
+        self.assertEquals(members, set())
+
+        # Add the group back into the directory
+        fieldName = self.directory.fieldName
+        yield self.directory.updateRecords(
+            (
+                TestRecord(
+                    self.directory,
+                    {
+                        fieldName.uid: u&quot;testgroup&quot;,
+                        fieldName.recordType: RecordType.group,
+                    }
+                ),
+            ),
+            create=True
+        )
+        group = yield self.directory.recordWithUID(u&quot;testgroup&quot;)
+        members = yield self.directory.recordsWithRecordType(RecordType.user)
+        yield group.setMembers(members)
+
+        txn = store.newTransaction()
+        yield self.groupCacher.refreshGroup(txn, u&quot;testgroup&quot;)
+        (
+            groupID, _ignore_name, membershipHash, _ignore_modified,
+            extant
+        ) = (yield txn.groupByUID(u&quot;testgroup&quot;))
+        yield txn.commit()
+
+        # Extant = True
+        self.assertTrue(extant)
+
+        # The list of members stored in the DB for this group has 100 users
+        txn = store.newTransaction()
+        members = yield txn.membersOfGroup(groupID)
+        yield txn.commit()
+        self.assertEquals(len(members), 100)
</ins></span></pre></div>
<a id="CalendarServertrunktxdavwhoutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/who/util.py (13682 => 13683)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/who/util.py        2014-06-25 02:24:15 UTC (rev 13682)
+++ CalendarServer/trunk/txdav/who/util.py        2014-06-25 02:26:01 UTC (rev 13683)
</span><span class="lines">@@ -172,8 +172,8 @@
</span><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">         elif &quot;inmemory&quot; in directoryType:
</span><del>-            from txdav.who.test.support import InMemoryDirectoryService
-            directory = InMemoryDirectoryService()
</del><ins>+            from txdav.who.test.support import CalendarInMemoryDirectoryService
+            directory = CalendarInMemoryDirectoryService()
</ins><span class="cx"> 
</span><span class="cx">         else:
</span><span class="cx">             log.error(&quot;Invalid DirectoryType: {dt}&quot;, dt=directoryType)
</span></span></pre>
</div>
</div>

</body>
</html>