<!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>[14417] CalendarServer/branches/users/cdaboo/pod2pod-migration</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/14417">14417</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2015-02-16 12:56:21 -0800 (Mon, 16 Feb 2015)</dd>
</dl>
<h3>Log Message</h3>
<pre>Checkpoint: re-factor groups/delegates store API to use DAL Record objects to make it easier to serialize/deserialize them for pod2pod.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationcalendarservertoolsprincipalspy">CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/principals.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationcalendarserverwebadminworkpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/webadmin/work.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationrequirementsstabletxt">CalendarServer/branches/users/cdaboo/pod2pod-migration/requirements-stable.txt</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoreschedulingtesttest_workpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/test/test_work.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoreschedulingworkpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/work.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoresqlpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoresql_externalpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingattachmentspy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/attachments.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingmigrationhome_syncpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingstore_apipy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingtesttest_store_apipy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingutilpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresqlpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresql_externalpy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhodelegatespy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhogroupspy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/groups.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_delegatespy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_delegates.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_group_attendeespy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_group_attendees.py</a></li>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_groupspy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_groups.py</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresql_directorypy">CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_directory.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationcalendarservertoolsprincipalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/principals.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/principals.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/tools/principals.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -775,15 +775,11 @@
</span><span class="cx"> groupUIDs.append(record.uid)
</span><span class="cx">
</span><span class="cx"> for groupUID in groupUIDs:
</span><del>- (
- groupID, name, _ignore_membershipHash, modified, _ignore_extant
- ) = yield txn.groupByUID(
- groupUID
- )
- print("Group: \"{name}\" ({uid})".format(name=name, uid=groupUID))
</del><ins>+ group = yield txn.groupByUID(groupUID)
+ print("Group: \"{name}\" ({uid})".format(name=group.name, uid=group.groupUID))
</ins><span class="cx">
</span><span class="cx"> for txt, readWrite in (("read-only", False), ("read-write", True)):
</span><del>- delegatorUIDs = yield txn.delegatorsToGroup(groupID, readWrite)
</del><ins>+ delegatorUIDs = yield txn.delegatorsToGroup(group.groupID, readWrite)
</ins><span class="cx"> for delegatorUID in delegatorUIDs:
</span><span class="cx"> delegator = yield directory.recordWithUID(delegatorUID)
</span><span class="cx"> print(
</span><span class="lines">@@ -793,12 +789,12 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> print("Group members:")
</span><del>- memberUIDs = yield txn.groupMemberUIDs(groupID)
</del><ins>+ memberUIDs = yield txn.groupMemberUIDs(group.groupID)
</ins><span class="cx"> for memberUID in memberUIDs:
</span><span class="cx"> record = yield directory.recordWithUID(memberUID)
</span><span class="cx"> print(prettyRecord(record))
</span><span class="cx">
</span><del>- print("Last cached: {} GMT".format(modified))
</del><ins>+ print("Last cached: {} GMT".format(group.modified))
</ins><span class="cx"> print()
</span><span class="cx">
</span><span class="cx"> yield txn.commit()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationcalendarserverwebadminworkpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/webadmin/work.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/webadmin/work.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/calendarserver/webadmin/work.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -157,13 +157,13 @@
</span><span class="cx"> if workType == PushNotificationWork:
</span><span class="cx"> attrs += ("pushID", "priority")
</span><span class="cx"> elif workType == ScheduleOrganizerWork:
</span><del>- attrs += ("icalendarUid", "attendeeCount")
</del><ins>+ attrs += ("icalendarUID", "attendeeCount")
</ins><span class="cx"> elif workType == ScheduleRefreshWork:
</span><del>- attrs += ("icalendarUid", "attendeeCount")
</del><ins>+ attrs += ("icalendarUID", "attendeeCount")
</ins><span class="cx"> elif workType == ScheduleReplyWork:
</span><del>- attrs += ("icalendarUid",)
</del><ins>+ attrs += ("icalendarUID",)
</ins><span class="cx"> elif workType == ScheduleAutoReplyWork:
</span><del>- attrs += ("icalendarUid",)
</del><ins>+ attrs += ("icalendarUID",)
</ins><span class="cx"> elif workType == GroupCacherPollingWork:
</span><span class="cx"> attrs += ()
</span><span class="cx"> elif workType == IMIPPollingWork:
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationrequirementsstabletxt"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/requirements-stable.txt (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/requirements-stable.txt        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/requirements-stable.txt        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -36,7 +36,7 @@
</span><span class="cx"> #pyOpenSSL
</span><span class="cx"> pycrypto==2.6.1
</span><span class="cx">
</span><del>- --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/trunk@14404#egg=twextpy
</del><ins>+ --editable svn+http://svn.calendarserver.org/repository/calendarserver/twext/branches/users/cdaboo/pod2pod-migration@14416#egg=twextpy
</ins><span class="cx"> cffi==0.8.6
</span><span class="cx"> pycparser==2.10
</span><span class="cx"> #twisted
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoreschedulingtesttest_workpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/test/test_work.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/test/test_work.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/test/test_work.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -250,7 +250,7 @@
</span><span class="cx">
</span><span class="cx"> work = yield jobs[0].workItem()
</span><span class="cx"> self.assertTrue(isinstance(work, ScheduleOrganizerWork))
</span><del>- self.assertEqual(work.icalendarUid, "12345-67890")
</del><ins>+ self.assertEqual(work.icalendarUID, "12345-67890")
</ins><span class="cx"> self.assertEqual(scheduleActionFromSQL[work.scheduleAction], "create")
</span><span class="cx">
</span><span class="cx"> yield work.delete()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoreschedulingworkpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/work.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/work.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/scheduling/work.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -77,7 +77,7 @@
</span><span class="cx">
</span><span class="cx"> baseargs = {
</span><span class="cx"> "jobID": kwargs.pop("jobID"),
</span><del>- "icalendarUid": kwargs.pop("icalendarUid"),
</del><ins>+ "icalendarUID": kwargs.pop("icalendarUID"),
</ins><span class="cx"> "workType": cls.workType()
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -121,7 +121,7 @@
</span><span class="cx"> # cause deadlocks if done in the wrong order
</span><span class="cx">
</span><span class="cx"> # Row level lock on this item
</span><del>- locked = yield self.baseWork.trylock(ScheduleWork.icalendarUid == self.icalendarUid)
</del><ins>+ locked = yield self.baseWork.trylock(ScheduleWork.icalendarUID == self.icalendarUID)
</ins><span class="cx"> if locked:
</span><span class="cx"> yield self.trylock()
</span><span class="cx"> returnValue(locked)
</span><span class="lines">@@ -136,7 +136,7 @@
</span><span class="cx"> """
</span><span class="cx"> self.__dict__["baseWork"] = baseWork
</span><span class="cx"> self.__dict__["jobID"] = baseWork.jobID
</span><del>- self.__dict__["icalendarUid"] = baseWork.icalendarUid
</del><ins>+ self.__dict__["icalendarUID"] = baseWork.icalendarUID
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def delete(self):
</span><span class="lines">@@ -174,7 +174,7 @@
</span><span class="cx"> if self.workType() == ScheduleOrganizerSendWork.workType():
</span><span class="cx"> all = yield self.baseWork.query(
</span><span class="cx"> self.transaction,
</span><del>- (ScheduleWork.icalendarUid == self.icalendarUid).And(ScheduleWork.workID != self.workID),
</del><ins>+ (ScheduleWork.icalendarUID == self.icalendarUID).And(ScheduleWork.workID != self.workID),
</ins><span class="cx"> order=ScheduleWork.workID,
</span><span class="cx"> limit=1,
</span><span class="cx"> )
</span><span class="lines">@@ -183,7 +183,7 @@
</span><span class="cx"> if work.workType == self.workType():
</span><span class="cx"> job = yield JobItem.load(self.transaction, work.jobID)
</span><span class="cx"> yield job.update(notBefore=datetime.datetime.utcnow())
</span><del>- log.debug("ScheduleOrganizerSendWork - promoted job: {id}, UID: '{uid}'", id=work.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleOrganizerSendWork - promoted job: {id}, UID: '{uid}'", id=work.workID, uid=self.icalendarUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="lines">@@ -323,7 +323,7 @@
</span><span class="cx"> proposal = (yield txn.enqueue(
</span><span class="cx"> cls,
</span><span class="cx"> notBefore=notBefore,
</span><del>- icalendarUid=uid,
</del><ins>+ icalendarUID=uid,
</ins><span class="cx"> scheduleAction=scheduleActionToSQL[action],
</span><span class="cx"> homeResourceID=home.id(),
</span><span class="cx"> resourceID=resource.id() if resource else None,
</span><span class="lines">@@ -347,10 +347,10 @@
</span><span class="cx"> calendar_old = Component.fromString(self.icalendarTextOld) if self.icalendarTextOld else None
</span><span class="cx"> calendar_new = Component.fromString(self.icalendarTextNew) if self.icalendarTextNew else None
</span><span class="cx">
</span><del>- log.debug("ScheduleOrganizerWork - running for ID: {id}, UID: {uid}, organizer: {org}", id=self.workID, uid=self.icalendarUid, org=organizer)
</del><ins>+ log.debug("ScheduleOrganizerWork - running for ID: {id}, UID: {uid}, organizer: {org}", id=self.workID, uid=self.icalendarUID, org=organizer)
</ins><span class="cx">
</span><span class="cx"> # We need to get the UID lock for implicit processing.
</span><del>- yield NamedLock.acquire(self.transaction, "ImplicitUIDLock:%s" % (hashlib.md5(self.icalendarUid).hexdigest(),))
</del><ins>+ yield NamedLock.acquire(self.transaction, "ImplicitUIDLock:%s" % (hashlib.md5(self.icalendarUID).hexdigest(),))
</ins><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
</span><span class="cx"> scheduler = ImplicitScheduler()
</span><span class="lines">@@ -359,7 +359,7 @@
</span><span class="cx"> scheduleActionFromSQL[self.scheduleAction],
</span><span class="cx"> home,
</span><span class="cx"> resource,
</span><del>- self.icalendarUid,
</del><ins>+ self.icalendarUID,
</ins><span class="cx"> calendar_old,
</span><span class="cx"> calendar_new,
</span><span class="cx"> self.smartMerge
</span><span class="lines">@@ -368,15 +368,15 @@
</span><span class="cx"> self._dequeued()
</span><span class="cx">
</span><span class="cx"> except Exception, e:
</span><del>- log.debug("ScheduleOrganizerWork - exception ID: {id}, UID: '{uid}', {err}", id=self.workID, uid=self.icalendarUid, err=str(e))
</del><ins>+ log.debug("ScheduleOrganizerWork - exception ID: {id}, UID: '{uid}', {err}", id=self.workID, uid=self.icalendarUID, err=str(e))
</ins><span class="cx"> log.debug(traceback.format_exc())
</span><span class="cx"> raise
</span><span class="cx"> except:
</span><del>- log.debug("ScheduleOrganizerWork - bare exception ID: {id}, UID: '{uid}'", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleOrganizerWork - bare exception ID: {id}, UID: '{uid}'", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx"> log.debug(traceback.format_exc())
</span><span class="cx"> raise
</span><span class="cx">
</span><del>- log.debug("ScheduleOrganizerWork - done for ID: {id}, UID: {uid}, organizer: {org}", id=self.workID, uid=self.icalendarUid, org=organizer)
</del><ins>+ log.debug("ScheduleOrganizerWork - done for ID: {id}, UID: {uid}, organizer: {org}", id=self.workID, uid=self.icalendarUID, org=organizer)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -418,7 +418,7 @@
</span><span class="cx"> proposal = (yield txn.enqueue(
</span><span class="cx"> cls,
</span><span class="cx"> notBefore=notBefore,
</span><del>- icalendarUid=uid,
</del><ins>+ icalendarUID=uid,
</ins><span class="cx"> scheduleAction=scheduleActionToSQL[action],
</span><span class="cx"> homeResourceID=home.id(),
</span><span class="cx"> resourceID=resource.id() if resource else None,
</span><span class="lines">@@ -449,13 +449,13 @@
</span><span class="cx"> log.debug(
</span><span class="cx"> "ScheduleOrganizerSendWork - running for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}",
</span><span class="cx"> id=self.workID,
</span><del>- uid=self.icalendarUid,
</del><ins>+ uid=self.icalendarUID,
</ins><span class="cx"> org=organizer,
</span><span class="cx"> att=self.attendee
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # We need to get the UID lock for implicit processing.
</span><del>- yield NamedLock.acquire(self.transaction, "ImplicitUIDLock:%s" % (hashlib.md5(self.icalendarUid).hexdigest(),))
</del><ins>+ yield NamedLock.acquire(self.transaction, "ImplicitUIDLock:%s" % (hashlib.md5(self.icalendarUID).hexdigest(),))
</ins><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler
</span><span class="cx"> scheduler = ImplicitScheduler()
</span><span class="lines">@@ -464,7 +464,7 @@
</span><span class="cx"> scheduleActionFromSQL[self.scheduleAction],
</span><span class="cx"> home,
</span><span class="cx"> resource,
</span><del>- self.icalendarUid,
</del><ins>+ self.icalendarUID,
</ins><span class="cx"> organizer,
</span><span class="cx"> self.attendee,
</span><span class="cx"> itipmsg,
</span><span class="lines">@@ -486,18 +486,18 @@
</span><span class="cx"> self._dequeued()
</span><span class="cx">
</span><span class="cx"> except Exception, e:
</span><del>- log.debug("ScheduleOrganizerSendWork - exception ID: {id}, UID: '{uid}', {err}", id=self.workID, uid=self.icalendarUid, err=str(e))
</del><ins>+ log.debug("ScheduleOrganizerSendWork - exception ID: {id}, UID: '{uid}', {err}", id=self.workID, uid=self.icalendarUID, err=str(e))
</ins><span class="cx"> log.debug(traceback.format_exc())
</span><span class="cx"> raise
</span><span class="cx"> except:
</span><del>- log.debug("ScheduleOrganizerSendWork - bare exception ID: {id}, UID: '{uid}'", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleOrganizerSendWork - bare exception ID: {id}, UID: '{uid}'", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx"> log.debug(traceback.format_exc())
</span><span class="cx"> raise
</span><span class="cx">
</span><span class="cx"> log.debug(
</span><span class="cx"> "ScheduleOrganizerSendWork - for ID: {id}, UID: {uid}, organizer: {org}, attendee: {att}",
</span><span class="cx"> id=self.workID,
</span><del>- uid=self.icalendarUid,
</del><ins>+ uid=self.icalendarUID,
</ins><span class="cx"> org=organizer,
</span><span class="cx"> att=self.attendee
</span><span class="cx"> )
</span><span class="lines">@@ -521,7 +521,7 @@
</span><span class="cx"> proposal = (yield txn.enqueue(
</span><span class="cx"> cls,
</span><span class="cx"> notBefore=notBefore,
</span><del>- icalendarUid=uid,
</del><ins>+ icalendarUID=uid,
</ins><span class="cx"> homeResourceID=home.id(),
</span><span class="cx"> resourceID=resource.id() if resource else None,
</span><span class="cx"> itipMsg=itipmsg.getTextWithTimezones(includeTimezones=not config.EnableTimezonesByReference),
</span><span class="lines">@@ -649,7 +649,7 @@
</span><span class="cx"> notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=config.Scheduling.Options.WorkQueues.AttendeeRefreshBatchDelaySeconds)
</span><span class="cx"> proposal = (yield txn.enqueue(
</span><span class="cx"> cls,
</span><del>- icalendarUid=organizer_resource.uid(),
</del><ins>+ icalendarUID=organizer_resource.uid(),
</ins><span class="cx"> homeResourceID=organizer_resource._home.id(),
</span><span class="cx"> resourceID=organizer_resource.id(),
</span><span class="cx"> attendeeCount=len(attendees),
</span><span class="lines">@@ -676,7 +676,7 @@
</span><span class="cx"> log.debug("Schedule refresh for resource-id: {rid} - ignored", rid=self.resourceID)
</span><span class="cx"> returnValue(None)
</span><span class="cx">
</span><del>- log.debug("ScheduleRefreshWork - running for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleRefreshWork - running for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx">
</span><span class="cx"> # Get the unique list of pending attendees and split into batch to process
</span><span class="cx"> # TODO: do a DELETE ... and rownum <= N returning attendee - but have to fix Oracle to
</span><span class="lines">@@ -707,7 +707,7 @@
</span><span class="cx"> notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=config.Scheduling.Options.WorkQueues.AttendeeRefreshBatchIntervalSeconds)
</span><span class="cx"> yield self.transaction.enqueue(
</span><span class="cx"> self.__class__,
</span><del>- icalendarUid=self.icalendarUid,
</del><ins>+ icalendarUID=self.icalendarUID,
</ins><span class="cx"> homeResourceID=self.homeResourceID,
</span><span class="cx"> resourceID=self.resourceID,
</span><span class="cx"> attendeeCount=len(pendingAttendees),
</span><span class="lines">@@ -721,7 +721,7 @@
</span><span class="cx">
</span><span class="cx"> self._dequeued()
</span><span class="cx">
</span><del>- log.debug("ScheduleRefreshWork - done for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleRefreshWork - done for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -790,7 +790,7 @@
</span><span class="cx"> notBefore = datetime.datetime.utcnow() + datetime.timedelta(seconds=config.Scheduling.Options.WorkQueues.AutoReplyDelaySeconds)
</span><span class="cx"> proposal = (yield txn.enqueue(
</span><span class="cx"> cls,
</span><del>- icalendarUid=resource.uid(),
</del><ins>+ icalendarUID=resource.uid(),
</ins><span class="cx"> homeResourceID=resource._home.id(),
</span><span class="cx"> resourceID=resource.id(),
</span><span class="cx"> partstat=partstat,
</span><span class="lines">@@ -803,7 +803,7 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def doWork(self):
</span><span class="cx">
</span><del>- log.debug("ScheduleAutoReplyWork - running for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleAutoReplyWork - running for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx">
</span><span class="cx"> # Delete all other work items with the same pushID
</span><span class="cx"> yield Delete(
</span><span class="lines">@@ -816,7 +816,7 @@
</span><span class="cx">
</span><span class="cx"> self._dequeued()
</span><span class="cx">
</span><del>- log.debug("ScheduleAutoReplyWork - done for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUid)
</del><ins>+ log.debug("ScheduleAutoReplyWork - done for ID: {id}, UID: {uid}", id=self.workID, uid=self.icalendarUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -1878,8 +1878,8 @@
</span><span class="cx">
</span><span class="cx"> # First check that the actual group membership has changed
</span><span class="cx"> if (yield self.updateShareeGroupLink(groupUID)):
</span><del>- groupID = (yield self._txn.groupByUID(groupUID))[0]
- memberUIDs = yield self._txn.groupMemberUIDs(groupID)
</del><ins>+ group = yield self._txn.groupByUID(groupUID)
+ memberUIDs = yield self._txn.groupMemberUIDs(group.groupID)
</ins><span class="cx"> boundUIDs = set()
</span><span class="cx">
</span><span class="cx"> home = self._homeSchema
</span><span class="lines">@@ -2029,39 +2029,36 @@
</span><span class="cx"> update schema.GROUP_SHAREE
</span><span class="cx"> """
</span><span class="cx"> changed = False
</span><del>- (
- groupID, _ignore_name, membershipHash, _ignore_modDate,
- _ignore_extant
- ) = yield self._txn.groupByUID(groupUID)
</del><ins>+ group = yield self._txn.groupByUID(groupUID)
</ins><span class="cx">
</span><span class="cx"> gs = schema.GROUP_SHAREE
</span><span class="cx"> rows = yield Select(
</span><span class="cx"> [gs.MEMBERSHIP_HASH, gs.GROUP_BIND_MODE],
</span><span class="cx"> From=gs,
</span><span class="cx"> Where=(gs.CALENDAR_ID == self._resourceID).And(
</span><del>- gs.GROUP_ID == groupID)
</del><ins>+ gs.GROUP_ID == group.groupID)
</ins><span class="cx"> ).on(self._txn)
</span><span class="cx"> if rows:
</span><span class="cx"> [[gsMembershipHash, gsMode]] = rows
</span><span class="cx"> updateMap = {}
</span><del>- if gsMembershipHash != membershipHash:
- updateMap[gs.MEMBERSHIP_HASH] = membershipHash
</del><ins>+ if gsMembershipHash != group.membershipHash:
+ updateMap[gs.MEMBERSHIP_HASH] = group.membershipHash
</ins><span class="cx"> if mode is not None and gsMode != mode:
</span><span class="cx"> updateMap[gs.GROUP_BIND_MODE] = mode
</span><span class="cx"> if updateMap:
</span><span class="cx"> yield Update(
</span><span class="cx"> updateMap,
</span><span class="cx"> Where=(gs.CALENDAR_ID == self._resourceID).And(
</span><del>- gs.GROUP_ID == groupID
</del><ins>+ gs.GROUP_ID == group.groupID
</ins><span class="cx"> )
</span><span class="cx"> ).on(self._txn)
</span><span class="cx"> changed = True
</span><span class="cx"> else:
</span><span class="cx"> yield Insert({
</span><del>- gs.MEMBERSHIP_HASH: membershipHash,
</del><ins>+ gs.MEMBERSHIP_HASH: group.membershipHash,
</ins><span class="cx"> gs.GROUP_BIND_MODE: mode,
</span><span class="cx"> gs.CALENDAR_ID: self._resourceID,
</span><del>- gs.GROUP_ID: groupID,
</del><ins>+ gs.GROUP_ID: group.groupID,
</ins><span class="cx"> }).on(self._txn)
</span><span class="cx"> changed = True
</span><span class="cx">
</span><span class="lines">@@ -2148,8 +2145,8 @@
</span><span class="cx">
</span><span class="cx"> # invite every member of group
</span><span class="cx"> shareeViews = []
</span><del>- groupID = (yield self._txn.groupByUID(shareeUID))[0]
- memberUIDs = yield self._txn.groupMemberUIDs(groupID)
</del><ins>+ group = yield self._txn.groupByUID(shareeUID)
+ memberUIDs = yield self._txn.groupMemberUIDs(group.groupID)
</ins><span class="cx"> for memberUID in memberUIDs:
</span><span class="cx"> if memberUID != self._home.uid():
</span><span class="cx"> shareeView = yield self.shareeView(memberUID)
</span><span class="lines">@@ -2496,9 +2493,9 @@
</span><span class="cx"> groupRecord = yield self.directoryService().recordWithCalendarUserAddress(groupCUA)
</span><span class="cx"> if groupRecord:
</span><span class="cx"> # get members
</span><del>- groupID = (yield self._txn.groupByUID(groupRecord.uid))[0]
- if groupID is not None:
- members = yield self._txn.groupMembers(groupID)
</del><ins>+ group = yield self._txn.groupByUID(groupRecord.uid)
+ if group is not None:
+ members = yield self._txn.groupMembers(group.groupID)
</ins><span class="cx"> groupCUAToAttendeeMemberPropMap[groupRecord.canonicalCalendarUserAddress()] = tuple(
</span><span class="cx"> [member.attendeeProperty(params={"MEMBER": groupCUA}) for member in sorted(members, key=lambda x: x.uid)]
</span><span class="cx"> )
</span><span class="lines">@@ -2551,26 +2548,23 @@
</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,
- _ignore_extant
- ) = yield self._txn.groupByUID(groupUID)
</del><ins>+ group = yield self._txn.groupByUID(groupUID)
</ins><span class="cx">
</span><span class="cx"> ga = schema.GROUP_ATTENDEE
</span><del>- if groupID in groupIDToMembershipHashMap:
- if groupIDToMembershipHashMap[groupID] != membershipHash:
</del><ins>+ if group.groupID in groupIDToMembershipHashMap:
+ if groupIDToMembershipHashMap[group.groupID] != group.membershipHash:
</ins><span class="cx"> yield Update(
</span><del>- {ga.MEMBERSHIP_HASH: membershipHash, },
</del><ins>+ {ga.MEMBERSHIP_HASH: group.membershipHash, },
</ins><span class="cx"> Where=(ga.RESOURCE_ID == self._resourceID).And(
</span><del>- ga.GROUP_ID == groupID)
</del><ins>+ ga.GROUP_ID == group.groupID)
</ins><span class="cx"> ).on(self._txn)
</span><span class="cx"> changed = True
</span><del>- del groupIDToMembershipHashMap[groupID]
</del><ins>+ del groupIDToMembershipHashMap[group.groupID]
</ins><span class="cx"> else:
</span><span class="cx"> yield Insert({
</span><span class="cx"> ga.RESOURCE_ID: self._resourceID,
</span><del>- ga.GROUP_ID: groupID,
- ga.MEMBERSHIP_HASH: membershipHash,
</del><ins>+ ga.GROUP_ID: group.groupID,
+ ga.MEMBERSHIP_HASH: group.membershipHash,
</ins><span class="cx"> }).on(self._txn)
</span><span class="cx"> changed = True
</span><span class="cx">
</span><span class="lines">@@ -5135,7 +5129,7 @@
</span><span class="cx"> setattr(self, attr, None)
</span><span class="cx">
</span><span class="cx">
</span><del>- def externalize(self):
</del><ins>+ def serialize(self):
</ins><span class="cx"> """
</span><span class="cx"> Create a dictionary mapping key attributes so this object can be sent over a cross-pod call
</span><span class="cx"> and reconstituted at the other end. Note that the other end may have a different schema so
</span><span class="lines">@@ -5145,9 +5139,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def internalize(cls, txn, mapping):
</del><ins>+ def deserialize(cls, txn, mapping):
</ins><span class="cx"> """
</span><del>- Given a mapping generated by L{externalize}, convert the values into an array of database
</del><ins>+ Given a mapping generated by L{serialize}, convert the values into an array of database
</ins><span class="cx"> like items that conforms to the ordering of L{_allColumns} so it can be fed into L{makeClass}.
</span><span class="cx"> Note that there may be a schema mismatch with the external data, so treat missing items as
</span><span class="cx"> C{None} and ignore extra items.
</span><span class="lines">@@ -5288,7 +5282,7 @@
</span><span class="cx"> returnValue(cls.makeClass(home._txn, rows[0]) if len(rows) == 1 else None)
</span><span class="cx">
</span><span class="cx">
</span><del>- def externalize(self):
</del><ins>+ def serialize(self):
</ins><span class="cx"> """
</span><span class="cx"> Create a dictionary mapping key attributes so this object can be sent over a cross-pod call
</span><span class="cx"> and reconstituted at the other end. Note that the other end may have a different schema so
</span><span class="lines">@@ -5300,9 +5294,9 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def internalize(cls, txn, mapping):
</del><ins>+ def deserialize(cls, txn, mapping):
</ins><span class="cx"> """
</span><del>- Given a mapping generated by L{externalize}, convert the values into an array of database
</del><ins>+ Given a mapping generated by L{serialize}, convert the values into an array of database
</ins><span class="cx"> like items that conforms to the ordering of L{_allColumns} so it can be fed into L{makeClass}.
</span><span class="cx"> Note that there may be a schema mismatch with the external data, so treat missing items as
</span><span class="cx"> C{None} and ignore extra items.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcaldavdatastoresql_externalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/caldav/datastore/sql_external.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -69,7 +69,7 @@
</span><span class="cx"> Needed during migration.
</span><span class="cx"> """
</span><span class="cx"> raw_results = yield self._txn.store().conduit.send_home_get_all_attachments(self)
</span><del>- returnValue([Attachment.internalize(self._txn, attachment) for attachment in raw_results])
</del><ins>+ returnValue([Attachment.deserialize(self._txn, attachment) for attachment in raw_results])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -89,7 +89,7 @@
</span><span class="cx"> Needed during migration only.
</span><span class="cx"> """
</span><span class="cx"> raw_results = yield self._txn.store().conduit.send_home_get_attachment_links(self)
</span><del>- returnValue([AttachmentLink.internalize(self._txn, attachment) for attachment in raw_results])
</del><ins>+ returnValue([AttachmentLink.deserialize(self._txn, attachment) for attachment in raw_results])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def getAllDropboxIDs(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingattachmentspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/attachments.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/attachments.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/attachments.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -196,5 +196,5 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> # Calls on L{CommonHome} objects
</span><del>-UtilityConduitMixin._make_simple_action(AttachmentsConduitMixin, "home_get_all_attachments", "getAllAttachments", classMethod=False, transform_recv_result=UtilityConduitMixin._to_externalize_list)
-UtilityConduitMixin._make_simple_action(AttachmentsConduitMixin, "home_get_attachment_links", "getAttachmentLinks", classMethod=False, transform_recv_result=UtilityConduitMixin._to_externalize_list)
</del><ins>+UtilityConduitMixin._make_simple_action(AttachmentsConduitMixin, "home_get_all_attachments", "getAllAttachments", classMethod=False, transform_recv_result=UtilityConduitMixin._to_serialize_list)
+UtilityConduitMixin._make_simple_action(AttachmentsConduitMixin, "home_get_attachment_links", "getAttachmentLinks", classMethod=False, transform_recv_result=UtilityConduitMixin._to_serialize_list)
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingmigrationhome_syncpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/migration/home_sync.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -176,10 +176,10 @@
</span><span class="cx"> # TODO: Re-write attachment URIs - not sure if we need this as reverse proxy may take care of it
</span><span class="cx"> pass
</span><span class="cx">
</span><del>- # TODO: group attendee reconcile
</del><ins>+ # TODO: shared collections reconcile
</ins><span class="cx"> pass
</span><span class="cx">
</span><del>- # TODO: shared collections reconcile
</del><ins>+ # TODO: group attendee reconcile
</ins><span class="cx"> pass
</span><span class="cx">
</span><span class="cx"> # TODO: group sharee reconcile
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingstore_apipy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/store_api.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -133,8 +133,8 @@
</span><span class="cx">
</span><span class="cx"> # Calls on L{CommonHomeChild} objects
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_listobjects", "listObjects", classMethod=True)
</span><del>-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_loadallobjects", "loadAllObjects", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize_list)
-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_objectwith", "objectWith", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize)
</del><ins>+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_loadallobjects", "loadAllObjects", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize_list)
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_objectwith", "objectWith", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize)
</ins><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_movehere", "moveObjectResourceHere")
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_moveaway", "moveObjectResourceAway")
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_synctokenrevision", "syncTokenRevision")
</span><span class="lines">@@ -142,14 +142,14 @@
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "homechild_search", "search")
</span><span class="cx">
</span><span class="cx"> # Calls on L{CommonObjectResource} objects
</span><del>-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_loadallobjects", "loadAllObjects", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize_list)
-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_loadallobjectswithnames", "loadAllObjectsWithNames", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize_list)
</del><ins>+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_loadallobjects", "loadAllObjects", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize_list)
+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_loadallobjectswithnames", "loadAllObjectsWithNames", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize_list)
</ins><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_listobjects", "listObjects", classMethod=True)
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_countobjects", "countObjects", classMethod=True)
</span><del>-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_objectwith", "objectWith", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize)
</del><ins>+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_objectwith", "objectWith", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize)
</ins><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_resourcenameforuid", "resourceNameForUID", classMethod=True)
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_resourceuidforname", "resourceUIDForName", classMethod=True)
</span><del>-UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_create", "create", classMethod=True, transform_recv_result=UtilityConduitMixin._to_externalize)
</del><ins>+UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_create", "create", classMethod=True, transform_recv_result=UtilityConduitMixin._to_serialize)
</ins><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_setcomponent", "setComponent")
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_component", "component", transform_recv_result=UtilityConduitMixin._to_string)
</span><span class="cx"> UtilityConduitMixin._make_simple_action(StoreAPIConduitMixin, "objectresource_remove", "remove")
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingtesttest_store_apipy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/test/test_store_api.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -104,7 +104,7 @@
</span><span class="cx">
</span><span class="cx"> from txdav.caldav.datastore.sql_external import CalendarHomeExternal
</span><span class="cx"> recipient = yield txn.store().directoryService().recordWithUID(uid)
</span><del>- resourceID = yield txn.store().conduit.send_home_resource_id(self, recipient)
</del><ins>+ resourceID = yield txn.store().conduit.send_home_resource_id(txn, recipient)
</ins><span class="cx"> home = CalendarHomeExternal(txn, recipient.uid, resourceID) if resourceID is not None else None
</span><span class="cx"> if home:
</span><span class="cx"> home._childClass = home._childClass._externalClass
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastorepoddingutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/podding/util.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -212,19 +212,19 @@
</span><span class="cx"> # Transforms for returned data
</span><span class="cx"> #
</span><span class="cx"> @staticmethod
</span><del>- def _to_externalize(value):
</del><ins>+ def _to_serialize(value):
</ins><span class="cx"> """
</span><span class="cx"> Convert the value to the external (JSON-based) representation.
</span><span class="cx"> """
</span><del>- return value.externalize() if value is not None else None
</del><ins>+ return value.serialize() if value is not None else None
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span><del>- def _to_externalize_list(value):
</del><ins>+ def _to_serialize_list(value):
</ins><span class="cx"> """
</span><span class="cx"> Convert the value to the external (JSON-based) representation.
</span><span class="cx"> """
</span><del>- return [v.externalize() for v in value]
</del><ins>+ return [v.serialize() for v in value]
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @staticmethod
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -61,6 +61,8 @@
</span><span class="cx"> from txdav.carddav.iaddressbookstore import IAddressBookTransaction
</span><span class="cx"> from txdav.common.datastore.common import HomeChildBase
</span><span class="cx"> from txdav.common.datastore.podding.conduit import PoddingConduit
</span><ins>+from txdav.common.datastore.sql_directory import DelegatesAPIMixin, \
+ GroupsAPIMixin, GroupCacherAPIMixin
</ins><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_DIRECT, \
</span><span class="cx"> _BIND_MODE_INDIRECT, _BIND_MODE_OWN, _BIND_STATUS_ACCEPTED, \
</span><span class="cx"> _BIND_STATUS_DECLINED, _BIND_STATUS_DELETED, _BIND_STATUS_INVALID, \
</span><span class="lines">@@ -68,7 +70,7 @@
</span><span class="cx"> _HOME_STATUS_PURGING, schema, splitSQLString, _HOME_STATUS_MIGRATING
</span><span class="cx"> from txdav.common.icommondatastore import ConcurrentModification, \
</span><span class="cx"> RecordNotAllowedError, ExternalShareFailed, ShareNotAllowed, \
</span><del>- IndexedSearchException, NotFoundError
</del><ins>+ IndexedSearchException
</ins><span class="cx"> from txdav.common.icommondatastore import HomeChildNameNotAllowedError, \
</span><span class="cx"> HomeChildNameAlreadyExistsError, NoSuchHomeChildError, \
</span><span class="cx"> ObjectResourceNameNotAllowedError, ObjectResourceNameAlreadyExistsError, \
</span><span class="lines">@@ -80,7 +82,6 @@
</span><span class="cx"> from txdav.common.inotifications import INotificationCollection, \
</span><span class="cx"> INotificationObject
</span><span class="cx"> from txdav.idav import ChangeCategory
</span><del>-from txdav.who.delegates import Delegates
</del><span class="cx"> from txdav.xml import element
</span><span class="cx">
</span><span class="cx"> from uuid import uuid4, UUID
</span><span class="lines">@@ -88,7 +89,6 @@
</span><span class="cx"> from zope.interface import implements, directlyProvides
</span><span class="cx">
</span><span class="cx"> from collections import namedtuple
</span><del>-import datetime
</del><span class="cx"> import inspect
</span><span class="cx"> import itertools
</span><span class="cx"> import json
</span><span class="lines">@@ -565,7 +565,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><del>-class CommonStoreTransaction(object):
</del><ins>+class CommonStoreTransaction(GroupsAPIMixin, GroupCacherAPIMixin, DelegatesAPIMixin):
</ins><span class="cx"> """
</span><span class="cx"> Transaction implementation for SQL database.
</span><span class="cx"> """
</span><span class="lines">@@ -1036,1033 +1036,6 @@
</span><span class="cx"> # End of IMIP
</span><span class="cx">
</span><span class="cx">
</span><del>- # Groups
-
- @classproperty
- def _addGroupQuery(cls):
- gr = schema.GROUPS
- return Insert(
- {
- gr.NAME: Parameter("name"),
- gr.GROUP_UID: Parameter("groupUID"),
- gr.MEMBERSHIP_HASH: Parameter("membershipHash")
- },
- Return=gr.GROUP_ID
- )
-
-
- @classproperty
- def _updateGroupQuery(cls):
- gr = schema.GROUPS
- return Update(
- {
- gr.MEMBERSHIP_HASH: Parameter("membershipHash"),
- gr.NAME: Parameter("name"),
- gr.MODIFIED: Parameter("timestamp"),
- gr.EXTANT: Parameter("extant"),
- },
- Where=(gr.GROUP_UID == Parameter("groupUID"))
- )
-
-
- @classproperty
- def _groupByUID(cls):
- gr = schema.GROUPS
- return Select(
- [gr.GROUP_ID, gr.NAME, gr.MEMBERSHIP_HASH, gr.MODIFIED, gr.EXTANT],
- From=gr,
- Where=(gr.GROUP_UID == Parameter("groupUID"))
- )
-
-
- @classproperty
- def _groupByID(cls):
- gr = schema.GROUPS
- return Select(
- [gr.GROUP_UID, gr.NAME, gr.MEMBERSHIP_HASH, gr.EXTANT],
- From=gr,
- Where=(gr.GROUP_ID == Parameter("groupID"))
- )
-
-
- @classproperty
- def _deleteGroup(cls):
- gr = schema.GROUPS
- return Delete(
- From=gr,
- Where=(gr.GROUP_ID == Parameter("groupID"))
- )
-
-
- @inlineCallbacks
- def addGroup(self, groupUID, name, membershipHash):
- """
- @type groupUID: C{unicode}
- @type name: C{unicode}
- @type membershipHash: C{str}
- """
- record = yield self.directoryService().recordWithUID(groupUID)
- if record is None:
- returnValue(None)
-
- groupID = (yield self._addGroupQuery.on(
- self,
- name=name.encode("utf-8"),
- groupUID=groupUID.encode("utf-8"),
- membershipHash=membershipHash
- ))[0][0]
-
- yield self.refreshGroup(
- groupUID, record, groupID, name.encode("utf-8"), membershipHash, True
- )
- returnValue(groupID)
-
-
- def updateGroup(self, groupUID, name, membershipHash, extant=True):
- """
- @type groupUID: C{unicode}
- @type name: C{unicode}
- @type membershipHash: C{str}
- @type extant: C{boolean}
- """
- timestamp = datetime.datetime.utcnow()
- return self._updateGroupQuery.on(
- self,
- name=name.encode("utf-8"),
- groupUID=groupUID.encode("utf-8"),
- timestamp=timestamp,
- membershipHash=membershipHash,
- extant=(1 if extant else 0)
- )
-
-
- @inlineCallbacks
- def groupByUID(self, groupUID, create=True):
- """
- Return or create a record for the group UID.
-
- @type groupUID: C{unicode}
-
- @return: Deferred firing with tuple of group ID C{str}, group name
- C{unicode}, membership hash C{str}, modified timestamp, and
- extant C{boolean}
- """
- results = (
- yield self._groupByUID.on(
- self, groupUID=groupUID.encode("utf-8")
- )
- )
- if results:
- returnValue((
- results[0][0], # group id
- results[0][1].decode("utf-8"), # name
- results[0][2], # membership hash
- results[0][3], # modified timestamp
- bool(results[0][4]), # extant
- ))
- elif create:
- savepoint = SavepointAction("groupByUID")
- yield savepoint.acquire(self)
- try:
- groupID = yield self.addGroup(groupUID, u"", "")
- if groupID is None:
- # The record does not actually exist within the directory
- yield savepoint.release(self)
- returnValue((None, None, None, None, None))
-
- except Exception:
- yield savepoint.rollback(self)
- results = (
- yield self._groupByUID.on(
- self, groupUID=groupUID.encode("utf-8")
- )
- )
- if results:
- returnValue((
- results[0][0], # group id
- results[0][1].decode("utf-8"), # name
- results[0][2], # membership hash
- results[0][3], # modified timestamp
- bool(results[0][4]), # extant
- ))
- else:
- returnValue((None, None, None, None, None))
- else:
- yield savepoint.release(self)
- results = (
- yield self._groupByUID.on(
- self, groupUID=groupUID.encode("utf-8")
- )
- )
- if results:
- returnValue((
- results[0][0], # group id
- results[0][1].decode("utf-8"), # name
- results[0][2], # membership hash
- results[0][3], # modified timestamp
- bool(results[0][4]), # extant
- ))
- else:
- returnValue((None, None, None, None, None))
- else:
- returnValue((None, None, None, None, None))
-
-
- @inlineCallbacks
- def groupByID(self, groupID):
- """
- Given a group ID, return the group UID, or raise NotFoundError
-
- @type groupID: C{str}
- @return: Deferred firing with a tuple of group UID C{unicode},
- group name C{unicode}, membership hash C{str}, and extant C{boolean}
- """
- try:
- results = (yield self._groupByID.on(self, groupID=groupID))[0]
- if results:
- results = (
- results[0].decode("utf-8"),
- results[1].decode("utf-8"),
- results[2],
- bool(results[3])
- )
- returnValue(results)
- except IndexError:
- raise NotFoundError
-
-
- def deleteGroup(self, groupID):
- return self._deleteGroup.on(self, groupID=groupID)
-
- # End of Groups
-
-
- # Group Members
-
- @classproperty
- def _addMemberToGroupQuery(cls):
- gm = schema.GROUP_MEMBERSHIP
- return Insert(
- {
- gm.GROUP_ID: Parameter("groupID"),
- gm.MEMBER_UID: Parameter("memberUID")
- }
- )
-
-
- @classproperty
- def _removeMemberFromGroupQuery(cls):
- gm = schema.GROUP_MEMBERSHIP
- return Delete(
- From=gm,
- Where=(
- gm.GROUP_ID == Parameter("groupID")
- ).And(
- gm.MEMBER_UID == Parameter("memberUID")
- )
- )
-
-
- @classproperty
- def _selectGroupMembersQuery(cls):
- gm = schema.GROUP_MEMBERSHIP
- return Select(
- [gm.MEMBER_UID],
- From=gm,
- Where=(
- gm.GROUP_ID == Parameter("groupID")
- )
- )
-
-
- @classproperty
- def _selectGroupsForQuery(cls):
- gr = schema.GROUPS
- gm = schema.GROUP_MEMBERSHIP
-
- return Select(
- [gr.GROUP_UID],
- From=gr,
- Where=(
- gr.GROUP_ID.In(
- Select(
- [gm.GROUP_ID],
- From=gm,
- Where=(
- gm.MEMBER_UID == Parameter("uid")
- )
- )
- )
- )
- )
-
-
- def addMemberToGroup(self, memberUID, groupID):
- return self._addMemberToGroupQuery.on(
- self, groupID=groupID, memberUID=memberUID.encode("utf-8")
- )
-
-
- def removeMemberFromGroup(self, memberUID, groupID):
- return self._removeMemberFromGroupQuery.on(
- self, groupID=groupID, memberUID=memberUID.encode("utf-8")
- )
-
-
- @inlineCallbacks
- def groupMemberUIDs(self, groupID):
- """
- Returns the cached set of UIDs for members of the given groupID.
- Sub-groups are not returned in the results but their members are,
- because the group membership has already been expanded/flattened
- before storing in the db.
-
- @param groupID: the group ID
- @type groupID: C{int}
- @return: the set of member UIDs
- @rtype: a Deferred which fires with a set() of C{str} UIDs
- """
- members = set()
- results = (yield self._selectGroupMembersQuery.on(self, groupID=groupID))
- for row in results:
- members.add(row[0].decode("utf-8"))
- returnValue(members)
-
-
- @inlineCallbacks
- def refreshGroup(self, groupUID, record, groupID, cachedName, cachedMembershipHash, cachedExtant):
- """
- @param groupUID: the directory record
- @type groupUID: C{unicode}
- @param record: the directory record
- @type record: C{iDirectoryRecord}
- @param groupID: group resource id
- @type groupID: C{str}
- @param cachedName: group name in the database
- @type cachedName: C{unicode}
- @param cachedMembershipHash: membership hash in the database
- @type cachedMembershipHash: C{str}
- @param cachedExtant: extent field from in the database
- @type cachedExtant: C{bool}
-
- @return: Deferred firing with membershipChanged C{boolean}
-
- """
- if record is not None:
- memberUIDs = yield record.expandedMemberUIDs()
- name = record.displayName
- extant = True
- else:
- memberUIDs = frozenset()
- name = cachedName
- extant = False
-
- membershipHashContent = hashlib.md5()
- for memberUID in sorted(memberUIDs):
- membershipHashContent.update(str(memberUID))
- membershipHash = membershipHashContent.hexdigest()
-
- if cachedMembershipHash != membershipHash:
- membershipChanged = True
- log.debug(
- "Group '{group}' changed", group=name
- )
- else:
- membershipChanged = False
-
- if membershipChanged or extant != cachedExtant:
- # also updates group mod date
- yield self.updateGroup(
- groupUID, name, membershipHash, extant=extant
- )
-
- if membershipChanged:
- addedUIDs, removedUIDs = yield self.synchronizeMembers(groupID, set(memberUIDs))
- else:
- addedUIDs = removedUIDs = None
-
- returnValue((membershipChanged, addedUIDs, removedUIDs,))
-
-
- @inlineCallbacks
- def synchronizeMembers(self, groupID, newMemberUIDs):
- """
- Update the group membership table in the database to match the new membership list. This
- method will diff the existing set with the new set and apply the changes. It also calls out
- to a groupChanged() method with the set of added and removed members so that other modules
- that depend on groups can monitor the changes.
-
- @param groupID: group id of group to update
- @type groupID: L{str}
- @param newMemberUIDs: set of new member UIDs in the group
- @type newMemberUIDs: L{set} of L{str}
- """
- cachedMemberUIDs = (yield self.groupMemberUIDs(groupID))
-
- removed = cachedMemberUIDs - newMemberUIDs
- for memberUID in removed:
- yield self.removeMemberFromGroup(memberUID, groupID)
-
- added = newMemberUIDs - cachedMemberUIDs
- for memberUID in added:
- yield self.addMemberToGroup(memberUID, groupID)
-
- yield self.groupChanged(groupID, added, removed)
-
- returnValue((added, removed,))
-
-
- @inlineCallbacks
- def groupChanged(self, groupID, addedUIDs, removedUIDs):
- """
- Called when membership of a group changes.
-
- @param groupID: group id of group that changed
- @type groupID: L{str}
- @param addedUIDs: set of new member UIDs added to the group
- @type addedUIDs: L{set} of L{str}
- @param removedUIDs: set of old member UIDs removed from the group
- @type removedUIDs: L{set} of L{str}
- """
- yield Delegates.groupChanged(self, groupID, addedUIDs, removedUIDs)
-
-
- @inlineCallbacks
- def groupMembers(self, groupID):
- """
- The members of the given group as recorded in the db
- """
- members = set()
- memberUIDs = (yield self.groupMemberUIDs(groupID))
- for uid in memberUIDs:
- record = (yield self.directoryService().recordWithUID(uid))
- if record is not None:
- members.add(record)
- returnValue(members)
-
-
- @inlineCallbacks
- def groupUIDsFor(self, uid):
- """
- Returns the cached set of UIDs for the groups this given uid is
- a member of.
-
- @param uid: the uid
- @type uid: C{unicode}
- @return: the set of group IDs
- @rtype: a Deferred which fires with a set() of C{int} group IDs
- """
- groups = set()
- results = (
- yield self._selectGroupsForQuery.on(
- self, uid=uid.encode("utf-8")
- )
- )
- for row in results:
- groups.add(row[0].decode("utf-8"))
- returnValue(groups)
-
- # End of Group Members
-
- # Delegates
-
-
- @classproperty
- def _addDelegateQuery(cls):
- de = schema.DELEGATES
- return Insert({de.DELEGATOR: Parameter("delegator"),
- de.DELEGATE: Parameter("delegate"),
- de.READ_WRITE: Parameter("readWrite"),
- })
-
-
- @classproperty
- def _addDelegateGroupQuery(cls):
- ds = schema.DELEGATE_GROUPS
- return Insert({ds.DELEGATOR: Parameter("delegator"),
- ds.GROUP_ID: Parameter("groupID"),
- ds.READ_WRITE: Parameter("readWrite"),
- ds.IS_EXTERNAL: Parameter("isExternal"),
- })
-
-
- @classproperty
- def _removeDelegateQuery(cls):
- de = schema.DELEGATES
- return Delete(
- From=de,
- Where=(
- de.DELEGATOR == Parameter("delegator")
- ).And(
- de.DELEGATE == Parameter("delegate")
- ).And(
- de.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _removeDelegatesQuery(cls):
- de = schema.DELEGATES
- return Delete(
- From=de,
- Where=(
- de.DELEGATOR == Parameter("delegator")
- ).And(
- de.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _removeDelegateGroupQuery(cls):
- ds = schema.DELEGATE_GROUPS
- return Delete(
- From=ds,
- Where=(
- ds.DELEGATOR == Parameter("delegator")
- ).And(
- ds.GROUP_ID == Parameter("groupID")
- ).And(
- ds.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _removeDelegateGroupsQuery(cls):
- ds = schema.DELEGATE_GROUPS
- return Delete(
- From=ds,
- Where=(
- ds.DELEGATOR == Parameter("delegator")
- ).And(
- ds.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _selectDelegatesQuery(cls):
- de = schema.DELEGATES
- return Select(
- [de.DELEGATE],
- From=de,
- Where=(
- de.DELEGATOR == Parameter("delegator")
- ).And(
- de.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _selectDelegatorsToGroupQuery(cls):
- dg = schema.DELEGATE_GROUPS
- return Select(
- [dg.DELEGATOR],
- From=dg,
- Where=(
- dg.GROUP_ID == Parameter("delegateGroup")
- ).And(
- dg.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _selectDelegateGroupsQuery(cls):
- ds = schema.DELEGATE_GROUPS
- gr = schema.GROUPS
-
- return Select(
- [gr.GROUP_UID],
- From=gr,
- Where=(
- gr.GROUP_ID.In(
- Select(
- [ds.GROUP_ID],
- From=ds,
- Where=(
- ds.DELEGATOR == Parameter("delegator")
- ).And(
- ds.READ_WRITE == Parameter("readWrite")
- )
- )
- )
- )
- )
-
-
- @classproperty
- def _selectDirectDelegatorsQuery(cls):
- de = schema.DELEGATES
- return Select(
- [de.DELEGATOR],
- From=de,
- Where=(
- de.DELEGATE == Parameter("delegate")
- ).And(
- de.READ_WRITE == Parameter("readWrite")
- )
- )
-
-
- @classproperty
- def _selectIndirectDelegatorsQuery(cls):
- dg = schema.DELEGATE_GROUPS
- gm = schema.GROUP_MEMBERSHIP
-
- return Select(
- [dg.DELEGATOR],
- From=dg,
- Where=(
- dg.GROUP_ID.In(
- Select(
- [gm.GROUP_ID],
- From=gm,
- Where=(gm.MEMBER_UID == Parameter("delegate"))
- )
- ).And(
- dg.READ_WRITE == Parameter("readWrite")
- )
- )
- )
-
-
- @classproperty
- def _selectIndirectDelegatesQuery(cls):
- dg = schema.DELEGATE_GROUPS
- gm = schema.GROUP_MEMBERSHIP
-
- return Select(
- [gm.MEMBER_UID],
- From=gm,
- Where=(
- gm.GROUP_ID.In(
- Select(
- [dg.GROUP_ID],
- From=dg,
- Where=(dg.DELEGATOR == Parameter("delegator")).And(
- dg.READ_WRITE == Parameter("readWrite"))
- )
- )
- )
- )
-
-
- @classproperty
- def _selectExternalDelegateGroupsQuery(cls):
- edg = schema.EXTERNAL_DELEGATE_GROUPS
- return Select(
- [edg.DELEGATOR, edg.GROUP_UID_READ, edg.GROUP_UID_WRITE],
- From=edg
- )
-
-
- @classproperty
- def _removeExternalDelegateGroupsPairQuery(cls):
- edg = schema.EXTERNAL_DELEGATE_GROUPS
- return Delete(
- From=edg,
- Where=(
- edg.DELEGATOR == Parameter("delegator")
- )
- )
-
-
- @classproperty
- def _storeExternalDelegateGroupsPairQuery(cls):
- edg = schema.EXTERNAL_DELEGATE_GROUPS
- return Insert(
- {
- edg.DELEGATOR: Parameter("delegator"),
- edg.GROUP_UID_READ: Parameter("readDelegate"),
- edg.GROUP_UID_WRITE: Parameter("writeDelegate"),
- }
- )
-
-
- @classproperty
- def _removeExternalDelegateGroupsQuery(cls):
- ds = schema.DELEGATE_GROUPS
- return Delete(
- From=ds,
- Where=(
- ds.DELEGATOR == Parameter("delegator")
- ).And(
- ds.IS_EXTERNAL == 1
- )
- )
-
-
- @inlineCallbacks
- def addDelegate(self, delegator, delegate, readWrite):
- """
- Adds a row to the DELEGATES table. The delegate should not be a
- group. To delegate to a group, call addDelegateGroup() instead.
-
- @param delegator: the UID of the delegator
- @type delegator: C{unicode}
- @param delegate: the UID of the delegate
- @type delegate: C{unicode}
- @param readWrite: grant read and write access if True, otherwise
- read-only access
- @type readWrite: C{boolean}
- """
-
- def _addDelegate(subtxn):
- return self._addDelegateQuery.on(
- subtxn,
- delegator=delegator.encode("utf-8"),
- delegate=delegate.encode("utf-8"),
- readWrite=1 if readWrite else 0
- )
-
- try:
- yield self.subtransaction(_addDelegate, retries=0, failureOK=True)
- except AllRetriesFailed:
- pass
-
-
- @inlineCallbacks
- def addDelegateGroup(self, delegator, delegateGroupID, readWrite,
- isExternal=False):
- """
- 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 UID of the delegator
- @type delegator: C{unicode}
- @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}
- """
-
- def _addDelegateGroup(subtxn):
- return self._addDelegateGroupQuery.on(
- subtxn,
- delegator=delegator.encode("utf-8"),
- groupID=delegateGroupID,
- readWrite=1 if readWrite else 0,
- isExternal=1 if isExternal else 0
- )
-
- try:
- yield self.subtransaction(_addDelegateGroup, retries=0, failureOK=True)
- except AllRetriesFailed:
- pass
-
-
- def removeDelegate(self, delegator, delegate, readWrite):
- """
- 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 UID of the delegator
- @type delegator: C{unicode}
- @param delegate: the UID of the delegate
- @type delegate: C{unicode}
- @param readWrite: remove read and write access if True, otherwise
- read-only access
- @type readWrite: C{boolean}
- """
- return self._removeDelegateQuery.on(
- self,
- delegator=delegator.encode("utf-8"),
- delegate=delegate.encode("utf-8"),
- readWrite=1 if readWrite else 0
- )
-
-
- def removeDelegates(self, delegator, readWrite):
- """
- Removes all rows for this delegator/readWrite combination from the
- DELEGATES table.
-
- @param delegator: the UID of the delegator
- @type delegator: C{unicode}
- @param readWrite: remove read and write access if True, otherwise
- read-only access
- @type readWrite: C{boolean}
- """
- return self._removeDelegatesQuery.on(
- self,
- delegator=delegator.encode("utf-8"),
- readWrite=1 if readWrite else 0
- )
-
-
- def removeDelegateGroup(self, delegator, delegateGroupID, readWrite):
- """
- 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 UID of the delegator
- @type delegator: C{unicode}
- @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}
- """
- return self._removeDelegateGroupQuery.on(
- self,
- delegator=delegator.encode("utf-8"),
- groupID=delegateGroupID,
- readWrite=1 if readWrite else 0
- )
-
-
- def removeDelegateGroups(self, delegator, readWrite):
- """
- Removes all rows for this delegator/readWrite combination from the
- DELEGATE_GROUPS table.
-
- @param delegator: the UID of the delegator
- @type delegator: C{unicode}
- @param readWrite: remove read and write access if True, otherwise
- read-only access
- @type readWrite: C{boolean}
- """
- return self._removeDelegateGroupsQuery.on(
- self,
- delegator=delegator.encode("utf-8"),
- readWrite=1 if readWrite else 0
- )
-
-
- @inlineCallbacks
- def delegates(self, delegator, readWrite, expanded=False):
- """
- Returns the UIDs of all delegates for the given delegator. If
- expanded is False, only the direct delegates (users and groups)
- are returned. If expanded is True, the expanded membership is
- returned, not including the groups themselves.
-
- @param delegator: the UID of the delegator
- @type delegator: C{unicode}
- @param readWrite: the access-type to check for; read and write
- access if True, otherwise read-only access
- @type readWrite: C{boolean}
- @returns: the UIDs of the delegates (for the specified access
- type)
- @rtype: a Deferred resulting in a set
- """
- delegates = set()
- delegatorU = delegator.encode("utf-8")
-
- # First get the direct delegates
- results = (
- yield self._selectDelegatesQuery.on(
- self,
- delegator=delegatorU,
- readWrite=1 if readWrite else 0
- )
- )
- delegates.update([row[0].decode("utf-8") for row in results])
-
- if expanded:
- # Get those who are in groups which have been delegated to
- results = (
- yield self._selectIndirectDelegatesQuery.on(
- self,
- delegator=delegatorU,
- readWrite=1 if readWrite else 0
- )
- )
- # Skip the delegator if they are in one of the groups
- delegates.update([row[0].decode("utf-8") for row in results if row[0] != delegatorU])
-
- else:
- # Get the directly-delegated-to groups
- results = (
- yield self._selectDelegateGroupsQuery.on(
- self,
- delegator=delegatorU,
- readWrite=1 if readWrite else 0
- )
- )
- delegates.update([row[0].decode("utf-8") for row in results])
-
- returnValue(delegates)
-
-
- @inlineCallbacks
- def delegators(self, delegate, readWrite):
- """
- Returns the UIDs of all delegators which have granted access to
- the given delegate, either directly or indirectly via groups.
-
- @param delegate: the UID of the delegate
- @type delegate: C{unicode}
- @param readWrite: the access-type to check for; read and write
- access if True, otherwise read-only access
- @type readWrite: C{boolean}
- @returns: the UIDs of the delegators (for the specified access
- type)
- @rtype: a Deferred resulting in a set
- """
- delegators = set()
- delegateU = delegate.encode("utf-8")
-
- # First get the direct delegators
- results = (
- yield self._selectDirectDelegatorsQuery.on(
- self,
- delegate=delegateU,
- readWrite=1 if readWrite else 0
- )
- )
- delegators.update([row[0].decode("utf-8") for row in results])
-
- # Finally get those who have delegated to groups the delegate
- # is a member of
- results = (
- yield self._selectIndirectDelegatorsQuery.on(
- self,
- delegate=delegateU,
- readWrite=1 if readWrite else 0
- )
- )
- # Skip the delegator if they are in one of the groups
- delegators.update([row[0].decode("utf-8") for row in results if row[0] != delegateU])
-
- returnValue(delegators)
-
-
- @inlineCallbacks
- def delegatorsToGroup(self, delegateGroupID, readWrite):
- """
- Return the UIDs of those who have delegated to the given group with the
- given access level.
-
- @param delegateGroupID: the group ID of the delegate group
- @type delegateGroupID: C{int}
- @param readWrite: the access-type to check for; read and write
- access if True, otherwise read-only access
- @type readWrite: C{boolean}
- @returns: the UIDs of the delegators (for the specified access
- type)
- @rtype: a Deferred resulting in a set
-
- """
- results = (
- yield self._selectDelegatorsToGroupQuery.on(
- self,
- delegateGroup=delegateGroupID,
- readWrite=1 if readWrite else 0
- )
- )
- delegators = set([row[0].decode("utf-8") for row in results])
- returnValue(delegators)
-
-
- @inlineCallbacks
- def allGroupDelegates(self):
- """
- Return the UIDs 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 UIDs of all delegated-to groups
- @rtype: a Deferred resulting in a set
- """
- gr = schema.GROUPS
- dg = schema.DELEGATE_GROUPS
-
- results = (yield Select(
- [gr.GROUP_UID],
- From=gr,
- Where=(gr.GROUP_ID.In(Select([dg.GROUP_ID], From=dg, Where=None)))
- ).on(self))
- delegates = set()
- for row in results:
- delegates.add(row[0].decode("utf-8"))
-
- returnValue(delegates)
-
-
- @inlineCallbacks
- def externalDelegates(self):
- """
- Returns a dictionary mapping delegate UIDs to (read-group, write-group)
- tuples, including only those assignments that originated from the
- directory.
-
- @returns: dictionary mapping delegator uid to (readDelegateUID,
- writeDelegateUID) tuples
- @rtype: a Deferred resulting in a dictionary
- """
- delegates = {}
-
- # Get the externally managed delegates (which are all groups)
- results = (yield self._selectExternalDelegateGroupsQuery.on(self))
- for delegator, readDelegateUID, writeDelegateUID in results:
- delegates[delegator.encode("utf-8")] = (
- readDelegateUID.encode("utf-8") if readDelegateUID else None,
- writeDelegateUID.encode("utf-8") if writeDelegateUID else None
- )
-
- returnValue(delegates)
-
-
- @inlineCallbacks
- def assignExternalDelegates(
- self, delegator, readDelegateGroupID, writeDelegateGroupID,
- readDelegateUID, writeDelegateUID
- ):
- """
- Update the external delegate group table so we can quickly identify
- diffs next time, and update the delegate group table itself
-
- @param delegator
- @type delegator: C{UUID}
- """
-
- # Delete existing external assignments for the delegator
- yield self._removeExternalDelegateGroupsQuery.on(
- self,
- delegator=str(delegator)
- )
-
- # Remove from the external comparison table
- yield self._removeExternalDelegateGroupsPairQuery.on(
- self,
- delegator=str(delegator)
- )
-
- # Store new assignments in the external comparison table
- if readDelegateUID or writeDelegateUID:
- readDelegateForDB = (
- readDelegateUID.encode("utf-8") if readDelegateUID else ""
- )
- writeDelegateForDB = (
- writeDelegateUID.encode("utf-8") if writeDelegateUID else ""
- )
- yield self._storeExternalDelegateGroupsPairQuery.on(
- self,
- delegator=str(delegator),
- readDelegate=readDelegateForDB,
- writeDelegate=writeDelegateForDB
- )
-
- # Apply new assignments
- if readDelegateGroupID is not None:
- yield self.addDelegateGroup(
- delegator, readDelegateGroupID, False, isExternal=True
- )
- if writeDelegateGroupID is not None:
- yield self.addDelegateGroup(
- delegator, writeDelegateGroupID, True, isExternal=True
- )
-
-
- # End of Delegates
-
-
</del><span class="cx"> def preCommit(self, operation):
</span><span class="cx"> """
</span><span class="cx"> Run things before C{commit}. (Note: only provided by SQL
</span><span class="lines">@@ -6033,7 +5006,7 @@
</span><span class="cx"> return self.ownerHome().externalClass()
</span><span class="cx">
</span><span class="cx">
</span><del>- def externalize(self):
</del><ins>+ def serialize(self):
</ins><span class="cx"> """
</span><span class="cx"> Create a dictionary mapping key attributes so this object can be sent over a cross-pod call
</span><span class="cx"> and reconstituted at the other end. Note that the other end may have a different schema so
</span><span class="lines">@@ -6048,9 +5021,9 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def internalize(cls, parent, mapping):
</del><ins>+ def deserialize(cls, parent, mapping):
</ins><span class="cx"> """
</span><del>- Given a mapping generated by L{externalize}, convert the values into an array of database
</del><ins>+ Given a mapping generated by L{serialize}, convert the values into an array of database
</ins><span class="cx"> like items that conforms to the ordering of L{_allColumns} so it can be fed into L{makeClass}.
</span><span class="cx"> Note that there may be a schema mismatch with the external data, so treat missing items as
</span><span class="cx"> C{None} and ignore extra items.
</span><span class="lines">@@ -7290,7 +6263,7 @@
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><del>- def externalize(self):
</del><ins>+ def serialize(self):
</ins><span class="cx"> """
</span><span class="cx"> Create a dictionary mapping key attributes so this object can be sent over a cross-pod call
</span><span class="cx"> and reconstituted at the other end. Note that the other end may have a different schema so
</span><span class="lines">@@ -7301,9 +6274,9 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def internalize(cls, parent, mapping):
</del><ins>+ def deserialize(cls, parent, mapping):
</ins><span class="cx"> """
</span><del>- Given a mapping generated by L{externalize}, convert the values into an array of database
</del><ins>+ Given a mapping generated by L{serialize}, convert the values into an array of database
</ins><span class="cx"> like items that conforms to the ordering of L{_allColumns} so it can be fed into L{makeClass}.
</span><span class="cx"> Note that there may be a schema mismatch with the external data, so treat missing items as
</span><span class="cx"> C{None} and ignore extra items.
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresql_directorypy"></a>
<div class="addfile"><h4>Added: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_directory.py (0 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_directory.py         (rev 0)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_directory.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -0,0 +1,773 @@
</span><ins>+# -*- test-case-name: twext.enterprise.dal.test.test_record -*-
+##
+# Copyright (c) 2015 Apple 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.
+##
+
+from twext.enterprise.dal.record import SerializableRecord, fromTable
+from twext.enterprise.dal.syntax import SavepointAction
+from twext.python.log import Logger
+from twisted.internet.defer import inlineCallbacks, returnValue
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.icommondatastore import AllRetriesFailed, NotFoundError
+import datetime
+import hashlib
+from txdav.who.delegates import Delegates
+
+log = Logger()
+
+"""
+Classes and methods that relate to directory objects in the SQL store. e.g.,
+delegates, groups etc
+"""
+
+class GroupsRecord(SerializableRecord, fromTable(schema.GROUPS)):
+ """
+ @DynamicAttrs
+ L{Record} for L{schema.GROUPS}.
+ """
+
+ @classmethod
+ def groupsForMember(cls, txn, memberUID):
+
+ return GroupsRecord.query(
+ txn,
+ GroupsRecord.groupID.In(
+ GroupMembershipRecord.queryExpr(
+ GroupMembershipRecord.memberUID == memberUID.encode("utf-8"),
+ attributes=(GroupMembershipRecord.groupID,),
+ )
+ ),
+ )
+
+
+
+class GroupMembershipRecord(SerializableRecord, fromTable(schema.GROUP_MEMBERSHIP)):
+ """
+ @DynamicAttrs
+ L{Record} for L{schema.GROUP_MEMBERSHIP}.
+ """
+ pass
+
+
+
+class DelegateRecord(SerializableRecord, fromTable(schema.DELEGATES)):
+ """
+ @DynamicAttrs
+ L{Record} for L{schema.DELEGATES}.
+ """
+ pass
+
+
+
+class DelegateGroupsRecord(SerializableRecord, fromTable(schema.DELEGATE_GROUPS)):
+ """
+ @DynamicAttrs
+ L{Record} for L{schema.DELEGATE_GROUPS}.
+ """
+
+ @classmethod
+ def allGroupDelegates(cls, txn):
+ """
+ Get the directly-delegated-to groups.
+ """
+
+ return GroupsRecord.query(
+ txn,
+ GroupsRecord.groupID.In(
+ DelegateGroupsRecord.queryExpr(
+ None,
+ attributes=(DelegateGroupsRecord.groupID,),
+ )
+ ),
+ )
+
+
+ @classmethod
+ def delegateGroups(cls, txn, delegator, readWrite):
+ """
+ Get the directly-delegated-to groups.
+ """
+
+ return GroupsRecord.query(
+ txn,
+ GroupsRecord.groupID.In(
+ DelegateGroupsRecord.queryExpr(
+ (DelegateGroupsRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateGroupsRecord.readWrite == (1 if readWrite else 0)
+ ),
+ attributes=(DelegateGroupsRecord.groupID,),
+ )
+ ),
+ )
+
+
+ @classmethod
+ def indirectDelegators(cls, txn, delegate, readWrite):
+ """
+ Get delegators who have delegated to groups the delegate is a member of.
+ """
+
+ return cls.query(
+ txn,
+ cls.groupID.In(
+ GroupMembershipRecord.queryExpr(
+ GroupMembershipRecord.memberUID == delegate.encode("utf-8"),
+ attributes=(GroupMembershipRecord.groupID,),
+ )
+ ).And(cls.readWrite == (1 if readWrite else 0)),
+ )
+
+
+ @classmethod
+ def indirectDelegates(cls, txn, delegator, readWrite):
+ """
+ Get delegates who are in groups which have been delegated to.
+ """
+
+ return GroupMembershipRecord.query(
+ txn,
+ GroupMembershipRecord.groupID.In(
+ DelegateGroupsRecord.queryExpr(
+ (DelegateGroupsRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateGroupsRecord.readWrite == (1 if readWrite else 0)
+ ),
+ attributes=(DelegateGroupsRecord.groupID,),
+ )
+ ),
+ )
+
+
+
+class ExternalDelegateGroupsRecord(SerializableRecord, fromTable(schema.EXTERNAL_DELEGATE_GROUPS)):
+ """
+ @DynamicAttrs
+ L{Record} for L{schema.EXTERNAL_DELEGATE_GROUPS}.
+ """
+ pass
+
+
+
+class GroupsAPIMixin(object):
+ """
+ A mixin for L{CommonStoreTransaction} that covers the groups API.
+ """
+
+ @inlineCallbacks
+ def addGroup(self, groupUID, name, membershipHash):
+ """
+ @type groupUID: C{unicode}
+ @type name: C{unicode}
+ @type membershipHash: C{str}
+ """
+ record = yield self.directoryService().recordWithUID(groupUID)
+ if record is None:
+ returnValue(None)
+
+ group = yield GroupsRecord.create(
+ self,
+ name=name.encode("utf-8"),
+ groupUID=groupUID.encode("utf-8"),
+ membershipHash=membershipHash,
+ )
+
+ yield self.refreshGroup(group, record)
+ returnValue(group)
+
+
+ def updateGroup(self, groupUID, name, membershipHash, extant=True):
+ """
+ @type groupUID: C{unicode}
+ @type name: C{unicode}
+ @type membershipHash: C{str}
+ @type extant: C{boolean}
+ """
+ timestamp = datetime.datetime.utcnow()
+ group = yield self.groupByUID(groupUID, create=False)
+ if group is not None:
+ yield group.update(
+ name=name.encode("utf-8"),
+ membershipHash=membershipHash,
+ extant=(1 if extant else 0),
+ modified=timestamp,
+ )
+
+
+ @inlineCallbacks
+ def groupByUID(self, groupUID, create=True):
+ """
+ Return or create a record for the group UID.
+
+ @type groupUID: C{unicode}
+
+ @return: Deferred firing with tuple of group ID C{str}, group name
+ C{unicode}, membership hash C{str}, modified timestamp, and
+ extant C{boolean}
+ """
+ results = yield GroupsRecord.query(
+ self,
+ GroupsRecord.groupUID == groupUID.encode("utf-8")
+ )
+ if results:
+ returnValue(results[0])
+ elif create:
+ savepoint = SavepointAction("groupByUID")
+ yield savepoint.acquire(self)
+ try:
+ group = yield self.addGroup(groupUID, u"", "")
+ if group is None:
+ # The record does not actually exist within the directory
+ yield savepoint.release(self)
+ returnValue(None)
+
+ except Exception:
+ yield savepoint.rollback(self)
+ results = yield GroupsRecord.query(
+ self,
+ GroupsRecord.groupUID == groupUID.encode("utf-8")
+ )
+ returnValue(results[0] if results else None)
+ else:
+ yield savepoint.release(self)
+ returnValue(group)
+ else:
+ returnValue(None)
+
+
+ @inlineCallbacks
+ def groupByID(self, groupID):
+ """
+ Given a group ID, return the group UID, or raise NotFoundError
+
+ @type groupID: C{str}
+ @return: Deferred firing with a tuple of group UID C{unicode},
+ group name C{unicode}, membership hash C{str}, and extant C{boolean}
+ """
+ results = yield GroupsRecord.query(
+ self,
+ GroupsRecord.groupID == groupID,
+ )
+ if results:
+ returnValue(results[0])
+ else:
+ raise NotFoundError
+
+
+
+class GroupCacherAPIMixin(object):
+ """
+ A mixin for L{CommonStoreTransaction} that covers the group cacher API.
+ """
+
+ def addMemberToGroup(self, memberUID, groupID):
+ return GroupMembershipRecord.create(self, groupID=groupID, memberUID=memberUID.encode("utf-8"))
+
+
+ def removeMemberFromGroup(self, memberUID, groupID):
+ return GroupMembershipRecord.deletematch(
+ self, groupID=groupID, memberUID=memberUID.encode("utf-8")
+ )
+
+
+ @inlineCallbacks
+ def groupMemberUIDs(self, groupID):
+ """
+ Returns the cached set of UIDs for members of the given groupID.
+ Sub-groups are not returned in the results but their members are,
+ because the group membership has already been expanded/flattened
+ before storing in the db.
+
+ @param groupID: the group ID
+ @type groupID: C{int}
+ @return: the set of member UIDs
+ @rtype: a Deferred which fires with a set() of C{str} UIDs
+ """
+
+ members = yield GroupMembershipRecord.query(self, GroupMembershipRecord.groupID == groupID)
+ returnValue(set([record.memberUID.decode("utf-8") for record in members]))
+
+
+ @inlineCallbacks
+ def refreshGroup(self, group, record):
+ """
+ @param group: the group record
+ @type group: L{GroupsRecord}
+ @param record: the directory record
+ @type record: C{iDirectoryRecord}
+
+ @return: Deferred firing with membershipChanged C{boolean}
+
+ """
+
+ if record is not None:
+ memberUIDs = yield record.expandedMemberUIDs()
+ name = record.displayName
+ extant = True
+ else:
+ memberUIDs = frozenset()
+ name = group.name
+ extant = False
+
+ membershipHashContent = hashlib.md5()
+ for memberUID in sorted(memberUIDs):
+ membershipHashContent.update(str(memberUID))
+ membershipHash = membershipHashContent.hexdigest()
+
+ if group.membershipHash != membershipHash:
+ membershipChanged = True
+ log.debug(
+ "Group '{group}' changed", group=name
+ )
+ else:
+ membershipChanged = False
+
+ if membershipChanged or extant != group.extant:
+ # also updates group mod date
+ yield group.update(
+ name=name,
+ membershipHash=membershipHash,
+ extant=(1 if extant else 0),
+ )
+
+ if membershipChanged:
+ addedUIDs, removedUIDs = yield self.synchronizeMembers(group.groupID, set(memberUIDs))
+ else:
+ addedUIDs = removedUIDs = None
+
+ returnValue((membershipChanged, addedUIDs, removedUIDs,))
+
+
+ @inlineCallbacks
+ def synchronizeMembers(self, groupID, newMemberUIDs):
+ """
+ Update the group membership table in the database to match the new membership list. This
+ method will diff the existing set with the new set and apply the changes. It also calls out
+ to a groupChanged() method with the set of added and removed members so that other modules
+ that depend on groups can monitor the changes.
+
+ @param groupID: group id of group to update
+ @type groupID: L{str}
+ @param newMemberUIDs: set of new member UIDs in the group
+ @type newMemberUIDs: L{set} of L{str}
+ """
+ cachedMemberUIDs = yield self.groupMemberUIDs(groupID)
+
+ removed = cachedMemberUIDs - newMemberUIDs
+ for memberUID in removed:
+ yield self.removeMemberFromGroup(memberUID, groupID)
+
+ added = newMemberUIDs - cachedMemberUIDs
+ for memberUID in added:
+ yield self.addMemberToGroup(memberUID, groupID)
+
+ yield self.groupChanged(groupID, added, removed)
+
+ returnValue((added, removed,))
+
+
+ @inlineCallbacks
+ def groupChanged(self, groupID, addedUIDs, removedUIDs):
+ """
+ Called when membership of a group changes.
+
+ @param groupID: group id of group that changed
+ @type groupID: L{str}
+ @param addedUIDs: set of new member UIDs added to the group
+ @type addedUIDs: L{set} of L{str}
+ @param removedUIDs: set of old member UIDs removed from the group
+ @type removedUIDs: L{set} of L{str}
+ """
+ yield Delegates.groupChanged(self, groupID, addedUIDs, removedUIDs)
+
+
+ @inlineCallbacks
+ def groupMembers(self, groupID):
+ """
+ The members of the given group as recorded in the db
+ """
+ members = set()
+ memberUIDs = (yield self.groupMemberUIDs(groupID))
+ for uid in memberUIDs:
+ record = (yield self.directoryService().recordWithUID(uid))
+ if record is not None:
+ members.add(record)
+ returnValue(members)
+
+
+ @inlineCallbacks
+ def groupUIDsFor(self, uid):
+ """
+ Returns the cached set of UIDs for the groups this given uid is
+ a member of.
+
+ @param uid: the uid
+ @type uid: C{unicode}
+ @return: the set of group IDs
+ @rtype: a Deferred which fires with a set() of C{int} group IDs
+ """
+ groups = yield GroupsRecord.groupsForMember(self, uid)
+ returnValue(set([group.groupUID.decode("utf-8") for group in groups]))
+
+
+
+class DelegatesAPIMixin(object):
+ """
+ A mixin for L{CommonStoreTransaction} that covers the delegates API.
+ """
+
+ @inlineCallbacks
+ def addDelegate(self, delegator, delegate, readWrite):
+ """
+ Adds a row to the DELEGATES table. The delegate should not be a
+ group. To delegate to a group, call addDelegateGroup() instead.
+
+ @param delegator: the UID of the delegator
+ @type delegator: C{unicode}
+ @param delegate: the UID of the delegate
+ @type delegate: C{unicode}
+ @param readWrite: grant read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+
+ def _addDelegate(subtxn):
+ return DelegateRecord.create(
+ subtxn,
+ delegator=delegator.encode("utf-8"),
+ delegate=delegate.encode("utf-8"),
+ readWrite=1 if readWrite else 0
+ )
+
+ try:
+ yield self.subtransaction(_addDelegate, retries=0, failureOK=True)
+ except AllRetriesFailed:
+ pass
+
+
+ @inlineCallbacks
+ def addDelegateGroup(self, delegator, delegateGroupID, readWrite,
+ isExternal=False):
+ """
+ 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 UID of the delegator
+ @type delegator: C{unicode}
+ @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}
+ """
+
+ def _addDelegateGroup(subtxn):
+ return DelegateGroupsRecord.create(
+ subtxn,
+ delegator=delegator.encode("utf-8"),
+ groupID=delegateGroupID,
+ readWrite=1 if readWrite else 0,
+ isExternal=1 if isExternal else 0
+ )
+
+ try:
+ yield self.subtransaction(_addDelegateGroup, retries=0, failureOK=True)
+ except AllRetriesFailed:
+ pass
+
+
+ def removeDelegate(self, delegator, delegate, readWrite):
+ """
+ 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 UID of the delegator
+ @type delegator: C{unicode}
+ @param delegate: the UID of the delegate
+ @type delegate: C{unicode}
+ @param readWrite: remove read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return DelegateRecord.deletesome(
+ self,
+ (DelegateRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateRecord.delegate == delegate.encode("utf-8")).And(
+ DelegateRecord.readWrite == (1 if readWrite else 0)
+ ),
+ )
+
+
+ def removeDelegates(self, delegator, readWrite):
+ """
+ Removes all rows for this delegator/readWrite combination from the
+ DELEGATES table.
+
+ @param delegator: the UID of the delegator
+ @type delegator: C{unicode}
+ @param readWrite: remove read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return DelegateRecord.deletesome(
+ self,
+ (DelegateRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateRecord.readWrite == (1 if readWrite else 0)
+ ),
+ )
+
+
+ def removeDelegateGroup(self, delegator, delegateGroupID, readWrite):
+ """
+ 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 UID of the delegator
+ @type delegator: C{unicode}
+ @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}
+ """
+ return DelegateGroupsRecord.deletesome(
+ self,
+ (DelegateGroupsRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateGroupsRecord.groupID == delegateGroupID).And(
+ DelegateGroupsRecord.readWrite == (1 if readWrite else 0)
+ ),
+ )
+
+
+ def removeDelegateGroups(self, delegator, readWrite):
+ """
+ Removes all rows for this delegator/readWrite combination from the
+ DELEGATE_GROUPS table.
+
+ @param delegator: the UID of the delegator
+ @type delegator: C{unicode}
+ @param readWrite: remove read and write access if True, otherwise
+ read-only access
+ @type readWrite: C{boolean}
+ """
+ return DelegateGroupsRecord.deletesome(
+ self,
+ (DelegateGroupsRecord.delegator == delegator.encode("utf-8")).And(
+ DelegateGroupsRecord.readWrite == (1 if readWrite else 0)
+ ),
+ )
+
+
+ @inlineCallbacks
+ def delegates(self, delegator, readWrite, expanded=False):
+ """
+ Returns the UIDs of all delegates for the given delegator. If
+ expanded is False, only the direct delegates (users and groups)
+ are returned. If expanded is True, the expanded membership is
+ returned, not including the groups themselves.
+
+ @param delegator: the UID of the delegator
+ @type delegator: C{unicode}
+ @param readWrite: the access-type to check for; read and write
+ access if True, otherwise read-only access
+ @type readWrite: C{boolean}
+ @returns: the UIDs of the delegates (for the specified access
+ type)
+ @rtype: a Deferred resulting in a set
+ """
+ delegates = set()
+ delegatorU = delegator.encode("utf-8")
+
+ # First get the direct delegates
+ results = yield DelegateRecord.query(
+ self,
+ (DelegateRecord.delegator == delegatorU).And(
+ DelegateRecord.readWrite == (1 if readWrite else 0)
+ )
+ )
+ delegates.update([record.delegate.decode("utf-8") for record in results])
+
+ if expanded:
+ # Get those who are in groups which have been delegated to
+ results = yield DelegateGroupsRecord.indirectDelegates(
+ self, delegator, readWrite
+ )
+ # Skip the delegator if they are in one of the groups
+ delegates.update([record.memberUID.decode("utf-8") for record in results if record.memberUID != delegatorU])
+
+ else:
+ # Get the directly-delegated-to groups
+ results = yield DelegateGroupsRecord.delegateGroups(
+ self, delegator, readWrite,
+ )
+ delegates.update([record.groupUID.decode("utf-8") for record in results])
+
+ returnValue(delegates)
+
+
+ @inlineCallbacks
+ def delegators(self, delegate, readWrite):
+ """
+ Returns the UIDs of all delegators which have granted access to
+ the given delegate, either directly or indirectly via groups.
+
+ @param delegate: the UID of the delegate
+ @type delegate: C{unicode}
+ @param readWrite: the access-type to check for; read and write
+ access if True, otherwise read-only access
+ @type readWrite: C{boolean}
+ @returns: the UIDs of the delegators (for the specified access
+ type)
+ @rtype: a Deferred resulting in a set
+ """
+ delegators = set()
+ delegateU = delegate.encode("utf-8")
+
+ # First get the direct delegators
+ results = yield DelegateRecord.query(
+ self,
+ (DelegateRecord.delegate == delegateU).And(
+ DelegateRecord.readWrite == (1 if readWrite else 0)
+ )
+ )
+ delegators.update([record.delegator.decode("utf-8") for record in results])
+
+ # Finally get those who have delegated to groups the delegate
+ # is a member of
+ results = yield DelegateGroupsRecord.indirectDelegators(
+ self, delegate, readWrite
+ )
+ # Skip the delegator if they are in one of the groups
+ delegators.update([record.delegator.decode("utf-8") for record in results if record.delegator != delegateU])
+
+ returnValue(delegators)
+
+
+ @inlineCallbacks
+ def delegatorsToGroup(self, delegateGroupID, readWrite):
+ """
+ Return the UIDs of those who have delegated to the given group with the
+ given access level.
+
+ @param delegateGroupID: the group ID of the delegate group
+ @type delegateGroupID: C{int}
+ @param readWrite: the access-type to check for; read and write
+ access if True, otherwise read-only access
+ @type readWrite: C{boolean}
+ @returns: the UIDs of the delegators (for the specified access
+ type)
+ @rtype: a Deferred resulting in a set
+
+ """
+ results = yield DelegateGroupsRecord.query(
+ self,
+ (DelegateGroupsRecord.groupID == delegateGroupID).And(
+ DelegateGroupsRecord.readWrite == (1 if readWrite else 0)
+ )
+ )
+ delegators = set([record.delegator.decode("utf-8") for record in results])
+ returnValue(delegators)
+
+
+ @inlineCallbacks
+ def allGroupDelegates(self):
+ """
+ Return the UIDs 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 UIDs of all delegated-to groups
+ @rtype: a Deferred resulting in a set
+ """
+
+ results = yield DelegateGroupsRecord.allGroupDelegates(self)
+ delegates = set([record.groupUID.decode("utf-8") for record in results])
+
+ returnValue(delegates)
+
+
+ @inlineCallbacks
+ def externalDelegates(self):
+ """
+ Returns a dictionary mapping delegate UIDs to (read-group, write-group)
+ tuples, including only those assignments that originated from the
+ directory.
+
+ @returns: dictionary mapping delegator uid to (readDelegateUID,
+ writeDelegateUID) tuples
+ @rtype: a Deferred resulting in a dictionary
+ """
+ delegates = {}
+
+ # Get the externally managed delegates (which are all groups)
+ results = yield ExternalDelegateGroupsRecord.all(self)
+ for record in results:
+ delegates[record.delegator.encode("utf-8")] = (
+ record.groupUIDRead.encode("utf-8") if record.groupUIDRead else None,
+ record.groupUIDWrite.encode("utf-8") if record.groupUIDWrite else None
+ )
+
+ returnValue(delegates)
+
+
+ @inlineCallbacks
+ def assignExternalDelegates(
+ self, delegator, readDelegateGroupID, writeDelegateGroupID,
+ readDelegateUID, writeDelegateUID
+ ):
+ """
+ Update the external delegate group table so we can quickly identify
+ diffs next time, and update the delegate group table itself
+
+ @param delegator
+ @type delegator: C{UUID}
+ """
+
+ # Delete existing external assignments for the delegator
+ yield DelegateGroupsRecord.deletesome(
+ self,
+ (DelegateGroupsRecord.delegator == str(delegator)).And(
+ DelegateGroupsRecord.isExternal == 1
+ )
+ )
+
+ # Remove from the external comparison table
+ yield ExternalDelegateGroupsRecord.deletesome(
+ self,
+ ExternalDelegateGroupsRecord.delegator == str(delegator)
+ )
+
+ # Store new assignments in the external comparison table
+ if readDelegateUID or writeDelegateUID:
+ readDelegateForDB = (
+ readDelegateUID.encode("utf-8") if readDelegateUID else ""
+ )
+ writeDelegateForDB = (
+ writeDelegateUID.encode("utf-8") if writeDelegateUID else ""
+ )
+ yield ExternalDelegateGroupsRecord.create(
+ self,
+ delegator=str(delegator),
+ groupUIDRead=readDelegateForDB,
+ groupUIDWrite=writeDelegateForDB,
+ )
+
+ # Apply new assignments
+ if readDelegateGroupID is not None:
+ yield self.addDelegateGroup(
+ delegator, readDelegateGroupID, False, isExternal=True
+ )
+ if writeDelegateGroupID is not None:
+ yield self.addDelegateGroup(
+ delegator, writeDelegateGroupID, True, isExternal=True
+ )
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavcommondatastoresql_externalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/common/datastore/sql_external.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -209,7 +209,7 @@
</span><span class="cx">
</span><span class="cx"> results = []
</span><span class="cx"> for mapping in raw_results:
</span><del>- child = yield cls.internalize(home, mapping)
</del><ins>+ child = yield cls.deserialize(home, mapping)
</ins><span class="cx"> results.append(child)
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="lines">@@ -220,7 +220,7 @@
</span><span class="cx"> mapping = yield home._txn.store().conduit.send_homechild_objectwith(home, name, resourceID, externalID, accepted)
</span><span class="cx">
</span><span class="cx"> if mapping:
</span><del>- child = yield cls.internalize(home, mapping)
</del><ins>+ child = yield cls.deserialize(home, mapping)
</ins><span class="cx"> returnValue(child)
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="lines">@@ -357,7 +357,7 @@
</span><span class="cx"> results = []
</span><span class="cx"> if mapping_list:
</span><span class="cx"> for mapping in mapping_list:
</span><del>- child = yield cls.internalize(parent, mapping)
</del><ins>+ child = yield cls.deserialize(parent, mapping)
</ins><span class="cx"> results.append(child)
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="lines">@@ -370,7 +370,7 @@
</span><span class="cx"> results = []
</span><span class="cx"> if mapping_list:
</span><span class="cx"> for mapping in mapping_list:
</span><del>- child = yield cls.internalize(parent, mapping)
</del><ins>+ child = yield cls.deserialize(parent, mapping)
</ins><span class="cx"> results.append(child)
</span><span class="cx"> returnValue(results)
</span><span class="cx">
</span><span class="lines">@@ -395,7 +395,7 @@
</span><span class="cx"> mapping = yield parent._txn.store().conduit.send_objectresource_objectwith(parent, name, uid, resourceID)
</span><span class="cx">
</span><span class="cx"> if mapping:
</span><del>- child = yield cls.internalize(parent, mapping)
</del><ins>+ child = yield cls.deserialize(parent, mapping)
</ins><span class="cx"> returnValue(child)
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span><span class="lines">@@ -421,7 +421,7 @@
</span><span class="cx"> mapping = yield parent._txn.store().conduit.send_objectresource_create(parent, name, str(component), options=options)
</span><span class="cx">
</span><span class="cx"> if mapping:
</span><del>- child = yield cls.internalize(parent, mapping)
</del><ins>+ child = yield cls.deserialize(parent, mapping)
</ins><span class="cx"> returnValue(child)
</span><span class="cx"> else:
</span><span class="cx"> returnValue(None)
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhodelegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/delegates.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/delegates.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/delegates.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -353,13 +353,8 @@
</span><span class="cx">
</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,
- _ignore_extant
- ) = yield txn.groupByUID(
- delegate.uid
- )
- yield txn.addDelegateGroup(delegator.uid, groupID, readWrite)
</del><ins>+ group = yield txn.groupByUID(delegate.uid)
+ yield txn.addDelegateGroup(delegator.uid, group.groupID, readWrite)
</ins><span class="cx"> else:
</span><span class="cx"> yield txn.addDelegate(delegator.uid, delegate.uid, readWrite)
</span><span class="cx">
</span><span class="lines">@@ -393,13 +388,8 @@
</span><span class="cx">
</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,
- _ignore_extant
- ) = yield txn.groupByUID(
- delegate.uid
- )
- yield txn.removeDelegateGroup(delegator.uid, groupID, readWrite)
</del><ins>+ group = yield txn.groupByUID(delegate.uid)
+ yield txn.removeDelegateGroup(delegator.uid, group.groupID, readWrite)
</ins><span class="cx"> else:
</span><span class="cx"> yield txn.removeDelegate(delegator.uid, delegate.uid, readWrite)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhogroupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/groups.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/groups.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/groups.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -85,7 +85,7 @@
</span><span class="cx">
</span><span class="cx"> class GroupRefreshWork(AggregatedWorkItem, fromTable(schema.GROUP_REFRESH_WORK)):
</span><span class="cx">
</span><del>- group = property(lambda self: (self.table.GROUP_UID == self.groupUid))
</del><ins>+ group = property(lambda self: (self.table.GROUP_UID == self.groupUID))
</ins><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def doWork(self):
</span><span class="lines">@@ -94,27 +94,27 @@
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> yield groupCacher.refreshGroup(
</span><del>- self.transaction, self.groupUid.decode("utf-8")
</del><ins>+ self.transaction, self.groupUID.decode("utf-8")
</ins><span class="cx"> )
</span><span class="cx"> except Exception, e:
</span><span class="cx"> log.error(
</span><span class="cx"> "Failed to refresh group {group} {err}",
</span><del>- group=self.groupUid, err=e
</del><ins>+ group=self.groupUID, err=e
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> else:
</span><span class="cx"> log.debug(
</span><span class="cx"> "Rescheduling group refresh for {group}: {when}",
</span><del>- group=self.groupUid,
</del><ins>+ group=self.groupUID,
</ins><span class="cx"> when=datetime.datetime.utcnow() + datetime.timedelta(seconds=10)
</span><span class="cx"> )
</span><del>- yield self.reschedule(self.transaction, 10, groupUID=self.groupUid)
</del><ins>+ yield self.reschedule(self.transaction, 10, groupUID=self.groupUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> class GroupDelegateChangesWork(AggregatedWorkItem, fromTable(schema.GROUP_DELEGATE_CHANGES_WORK)):
</span><span class="cx">
</span><del>- delegator = property(lambda self: (self.table.DELEGATOR_UID == self.delegatorUid))
</del><ins>+ delegator = property(lambda self: (self.table.DELEGATOR_UID == self.delegatorUID))
</ins><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def doWork(self):
</span><span class="lines">@@ -124,14 +124,14 @@
</span><span class="cx"> try:
</span><span class="cx"> yield groupCacher.applyExternalAssignments(
</span><span class="cx"> self.transaction,
</span><del>- self.delegatorUid.decode("utf-8"),
- self.readDelegateUid.decode("utf-8"),
- self.writeDelegateUid.decode("utf-8")
</del><ins>+ self.delegatorUID.decode("utf-8"),
+ self.readDelegateUID.decode("utf-8"),
+ self.writeDelegateUID.decode("utf-8")
</ins><span class="cx"> )
</span><span class="cx"> except Exception, e:
</span><span class="cx"> log.error(
</span><span class="cx"> "Failed to apply external delegates for {uid} {err}",
</span><del>- uid=self.delegatorUid, err=e
</del><ins>+ uid=self.delegatorUID, err=e
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -182,8 +182,8 @@
</span><span class="cx"> homeID = rows[0][0]
</span><span class="cx"> home = yield self.transaction.calendarHomeWithResourceID(homeID)
</span><span class="cx"> calendar = yield home.childWithID(self.calendarID)
</span><del>- groupUID = ((yield self.transaction.groupByID(self.groupID)))[0]
- yield calendar.reconcileGroupSharee(groupUID)
</del><ins>+ group = (yield self.transaction.groupByID(self.groupID))
+ yield calendar.reconcileGroupSharee(group.groupUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -302,7 +302,7 @@
</span><span class="cx"> # For each of those groups, create a per-group refresh work item
</span><span class="cx"> for groupUID in set(groupUIDs) - set(deletedGroupUIDs):
</span><span class="cx"> self.log.debug("Enqueuing group refresh for {u}", u=groupUID)
</span><del>- yield GroupRefreshWork.reschedule(txn, 0, groupUid=groupUID)
</del><ins>+ yield GroupRefreshWork.reschedule(txn, 0, groupUID=groupUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -335,9 +335,9 @@
</span><span class="cx"> )
</span><span class="cx"> else:
</span><span class="cx"> yield GroupDelegateChangesWork.reschedule(
</span><del>- txn, 0, delegatorUid=delegatorUID,
- readDelegateUid=readDelegateUID,
- writeDelegateUid=writeDelegateUID
</del><ins>+ txn, 0, delegatorUID=delegatorUID,
+ readDelegateUID=readDelegateUID,
+ writeDelegateUID=writeDelegateUID
</ins><span class="cx"> )
</span><span class="cx"> if removed:
</span><span class="cx"> for delegatorUID in removed:
</span><span class="lines">@@ -351,8 +351,8 @@
</span><span class="cx"> )
</span><span class="cx"> else:
</span><span class="cx"> yield GroupDelegateChangesWork.reschedule(
</span><del>- txn, 0, delegatorUid=delegatorUID,
- readDelegateUid="", writeDelegateUid=""
</del><ins>+ txn, 0, delegatorUID=delegatorUID,
+ readDelegateUID="", writeDelegateUID=""
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -367,26 +367,20 @@
</span><span class="cx"> readDelegateGroupID = writeDelegateGroupID = None
</span><span class="cx">
</span><span class="cx"> if readDelegateUID:
</span><del>- (
- readDelegateGroupID, _ignore_name, _ignore_hash,
- _ignore_modified, _ignore_extant
- ) = (
- yield txn.groupByUID(readDelegateUID)
- )
- if readDelegateGroupID is None:
</del><ins>+ readDelegateGroup = yield txn.groupByUID(readDelegateUID)
+ if readDelegateGroup is None:
</ins><span class="cx"> # The group record does not actually exist
</span><span class="cx"> readDelegateUID = None
</span><ins>+ else:
+ readDelegateGroupID = readDelegateGroup.groupID
</ins><span class="cx">
</span><span class="cx"> if writeDelegateUID:
</span><del>- (
- writeDelegateGroupID, _ignore_name, _ignore_hash,
- _ignore_modified, _ignore_extant
- ) = (
- yield txn.groupByUID(writeDelegateUID)
- )
- if writeDelegateGroupID is None:
</del><ins>+ writeDelegateGroup = yield txn.groupByUID(writeDelegateUID)
+ if writeDelegateGroup is None:
</ins><span class="cx"> # The group record does not actually exist
</span><span class="cx"> writeDelegateUID = None
</span><ins>+ else:
+ writeDelegateGroupID = writeDelegateGroup.groupID
</ins><span class="cx">
</span><span class="cx"> yield txn.assignExternalDelegates(
</span><span class="cx"> delegatorUID, readDelegateGroupID, writeDelegateGroupID,
</span><span class="lines">@@ -411,45 +405,36 @@
</span><span class="cx"> else:
</span><span class="cx"> self.log.debug("Got group record: {u}", u=record.uid)
</span><span class="cx">
</span><del>- (
- groupID, cachedName, cachedMembershipHash, _ignore_modified,
- cachedExtant
- ) = yield txn.groupByUID(
- groupUID,
- create=(record is not None)
- )
</del><ins>+ group = yield txn.groupByUID(groupUID, create=(record is not None))
</ins><span class="cx">
</span><del>- if groupID:
- membershipChanged, addedUIDs, removedUIDs = yield txn.refreshGroup(
- groupUID, record, groupID,
- cachedName, cachedMembershipHash, cachedExtant
- )
</del><ins>+ if group:
+ membershipChanged, addedUIDs, removedUIDs = yield txn.refreshGroup(group, record)
</ins><span class="cx">
</span><span class="cx"> if membershipChanged:
</span><span class="cx"> self.log.info(
</span><span class="cx"> "Membership changed for group {uid} {name}:\n\tadded {added}\n\tremoved {removed}",
</span><del>- uid=groupUID,
- name=cachedName,
</del><ins>+ uid=group.groupUID,
+ name=group.name,
</ins><span class="cx"> added=",".join(addedUIDs),
</span><span class="cx"> removed=",".join(removedUIDs),
</span><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Send cache change notifications
</span><span class="cx"> if self.cacheNotifier is not None:
</span><del>- self.cacheNotifier.changed(groupUID)
</del><ins>+ self.cacheNotifier.changed(group.groupUID)
</ins><span class="cx"> for uid in itertools.chain(addedUIDs, removedUIDs):
</span><span class="cx"> self.cacheNotifier.changed(uid)
</span><span class="cx">
</span><span class="cx"> # Notifier other store APIs of changes
</span><del>- wpsAttendee = yield self.scheduleGroupAttendeeReconciliations(txn, groupID)
- wpsShareee = yield self.scheduleGroupShareeReconciliations(txn, groupID)
</del><ins>+ wpsAttendee = yield self.scheduleGroupAttendeeReconciliations(txn, group.groupID)
+ wpsShareee = yield self.scheduleGroupShareeReconciliations(txn, group.groupID)
</ins><span class="cx">
</span><span class="cx"> returnValue(wpsAttendee + wpsShareee)
</span><span class="cx"> else:
</span><span class="cx"> self.log.debug(
</span><span class="cx"> "No membership change for group {uid} {name}",
</span><del>- uid=groupUID,
- name=cachedName
</del><ins>+ uid=group.groupUID,
+ name=group.name
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> returnValue(tuple())
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_delegatespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_delegates.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_delegates.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_delegates.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -19,6 +19,8 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> from txdav.common.datastore.sql import CommonStoreTransaction
</span><ins>+from txdav.common.datastore.sql_directory import DelegateRecord, \
+ DelegateGroupsRecord
</ins><span class="cx"> from txdav.who.delegates import Delegates, RecordType as DelegateRecordType
</span><span class="cx"> from txdav.who.groups import GroupCacher
</span><span class="cx"> from twext.who.idirectory import RecordType
</span><span class="lines">@@ -211,12 +213,9 @@
</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,
- _ignore_extant
- ) = (yield txn.groupByUID(group1.uid))
</del><ins>+ group = yield txn.groupByUID(group1.uid)
</ins><span class="cx"> _ignore_added, _ignore_removed = (
</span><del>- yield self.groupCacher.synchronizeMembers(txn, groupID, newSet)
</del><ins>+ yield self.groupCacher.synchronizeMembers(txn, group.groupID, newSet)
</ins><span class="cx"> )
</span><span class="cx"> delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True))
</span><span class="cx"> self.assertEquals(
</span><span class="lines">@@ -261,15 +260,14 @@
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- results = (
- yield txn._selectDelegatesQuery.on(
- txn,
- delegator=delegator.uid.encode("utf-8"),
- readWrite=1
</del><ins>+ results = yield DelegateRecord.query(
+ txn,
+ (DelegateRecord.delegator == delegator.uid.encode("utf-8")).And(
+ DelegateRecord.readWrite == 1
</ins><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> yield txn.commit()
</span><del>- self.assertEquals([["__sagen1__"]], results)
</del><ins>+ self.assertEquals(["__sagen1__", ], [record.delegate for record in results])
</ins><span class="cx">
</span><span class="cx"> # Delegate groups:
</span><span class="cx"> group1 = yield self.directory.recordWithUID(u"__top_group_1__")
</span><span class="lines">@@ -283,15 +281,13 @@
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> txn = self.store.newTransaction(label="test_noDuplication")
</span><del>- results = (
- yield txn._selectDelegateGroupsQuery.on(
- txn,
- delegator=delegator.uid.encode("utf-8"),
- readWrite=1
- )
</del><ins>+ results = yield DelegateGroupsRecord.delegateGroups(
+ txn,
+ delegator.uid,
+ True,
</ins><span class="cx"> )
</span><span class="cx"> yield txn.commit()
</span><del>- self.assertEquals([["__top_group_1__"]], results)
</del><ins>+ self.assertEquals(["__top_group_1__", ], [record.groupUID for record in results])
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_group_attendeespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_group_attendees.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_group_attendees.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_group_attendees.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -871,14 +871,11 @@
</span><span class="cx"> # finally, simulate an event that has become old
</span><span class="cx"> self.patch(CalendarDirectoryRecordMixin, "expandedMembers", unpatchedExpandedMembers)
</span><span class="cx">
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
- _ignore_extant
- ) = yield self.transactionUnderTest().groupByUID("group01")
</del><ins>+ group = yield self.transactionUnderTest().groupByUID("group01")
</ins><span class="cx"> ga = schema.GROUP_ATTENDEE
</span><span class="cx"> yield Insert({
</span><span class="cx"> ga.RESOURCE_ID: cobj._resourceID,
</span><del>- ga.GROUP_ID: groupID,
</del><ins>+ ga.GROUP_ID: group.groupID,
</ins><span class="cx"> ga.MEMBERSHIP_HASH: (-1),
</span><span class="cx"> }).on(self.transactionUnderTest())
</span><span class="cx"> wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group01")
</span><span class="lines">@@ -1033,14 +1030,11 @@
</span><span class="cx"> # finally, simulate an event that has become old
</span><span class="cx"> self.patch(CalendarDirectoryRecordMixin, "expandedMembers", unpatchedExpandedMembers)
</span><span class="cx">
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modDate,
- _ignore_extant
- ) = yield self.transactionUnderTest().groupByUID("group01")
</del><ins>+ group = yield self.transactionUnderTest().groupByUID("group01")
</ins><span class="cx"> ga = schema.GROUP_ATTENDEE
</span><span class="cx"> yield Insert({
</span><span class="cx"> ga.RESOURCE_ID: cobj._resourceID,
</span><del>- ga.GROUP_ID: groupID,
</del><ins>+ ga.GROUP_ID: group.groupID,
</ins><span class="cx"> ga.MEMBERSHIP_HASH: (-1),
</span><span class="cx"> }).on(self.transactionUnderTest())
</span><span class="cx"> wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group01")
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdaboopod2podmigrationtxdavwhotesttest_groupspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_groups.py (14416 => 14417)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_groups.py        2015-02-16 20:33:55 UTC (rev 14416)
+++ CalendarServer/branches/users/cdaboo/pod2pod-migration/txdav/who/test/test_groups.py        2015-02-16 20:56:21 UTC (rev 14417)
</span><span class="lines">@@ -67,27 +67,24 @@
</span><span class="cx"> record = yield self.directory.recordWithUID(u"__top_group_1__")
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, record.uid)
</span><span class="cx">
</span><del>- (
- groupID, _ignore_name, membershipHash, _ignore_modified,
- extant
- ) = (yield txn.groupByUID(record.uid))
</del><ins>+ group = (yield txn.groupByUID(record.uid))
</ins><span class="cx">
</span><del>- self.assertEquals(extant, True)
- self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")
</del><ins>+ self.assertEquals(group.extant, True)
+ self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4")
</ins><span class="cx">
</span><del>- groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID))
- self.assertEquals(groupUID, record.uid)
- self.assertEquals(name, u"Top Group 1")
- self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")
- self.assertEquals(extant, True)
</del><ins>+ group = yield txn.groupByID(group.groupID)
+ self.assertEquals(group.groupUID, record.uid)
+ self.assertEquals(group.name, u"Top Group 1")
+ self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4")
+ self.assertEquals(group.extant, True)
</ins><span class="cx">
</span><del>- members = (yield txn.groupMemberUIDs(groupID))
</del><ins>+ members = (yield txn.groupMemberUIDs(group.groupID))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']),
</span><span class="cx"> members
</span><span class="cx"> )
</span><span class="cx">
</span><del>- records = (yield self.groupCacher.cachedMembers(txn, groupID))
</del><ins>+ records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([r.uid for r in records]),
</span><span class="cx"> set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'])
</span><span class="lines">@@ -116,10 +113,7 @@
</span><span class="cx"> # Refresh the group so it's assigned a group_id
</span><span class="cx"> uid = u"__top_group_1__"
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- (
- groupID, name, _ignore_membershipHash, _ignore_modified,
- _ignore_extant
- ) = yield txn.groupByUID(uid)
</del><ins>+ group = 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">@@ -133,12 +127,12 @@
</span><span class="cx"> newSet.add(record.uid)
</span><span class="cx"> added, removed = (
</span><span class="cx"> yield self.groupCacher.synchronizeMembers(
</span><del>- txn, groupID, newSet
</del><ins>+ txn, group.groupID, newSet
</ins><span class="cx"> )
</span><span class="cx"> )
</span><span class="cx"> self.assertEquals(added, set(["__dre1__", ]))
</span><span class="cx"> self.assertEquals(removed, set(["__glyph1__", "__sagen1__", ]))
</span><del>- records = (yield self.groupCacher.cachedMembers(txn, groupID))
</del><ins>+ records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
</ins><span class="cx"> self.assertEquals(
</span><span class="cx"> set([r.shortNames[0] for r in records]),
</span><span class="cx"> set(["wsanchez1", "cdaboo1", "dre1"])
</span><span class="lines">@@ -146,11 +140,11 @@
</span><span class="cx">
</span><span class="cx"> # Remove all members
</span><span class="cx"> added, removed = (
</span><del>- yield self.groupCacher.synchronizeMembers(txn, groupID, set())
</del><ins>+ yield self.groupCacher.synchronizeMembers(txn, group.groupID, set())
</ins><span class="cx"> )
</span><span class="cx"> self.assertEquals(added, set())
</span><span class="cx"> self.assertEquals(removed, set(["__wsanchez1__", "__cdaboo1__", "__dre1__", ]))
</span><del>- records = (yield self.groupCacher.cachedMembers(txn, groupID))
</del><ins>+ records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
</ins><span class="cx"> self.assertEquals(len(records), 0)
</span><span class="cx">
</span><span class="cx"> yield txn.commit()
</span><span class="lines">@@ -168,12 +162,12 @@
</span><span class="cx"> uid = u"__top_group_1__"
</span><span class="cx"> hash = "553eb54e3bbb26582198ee04541dbee4"
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- _ignore_extant
- ) = yield txn.groupByUID(uid)
- results = yield txn.groupByID(groupID)
- self.assertEquals((uid, u"Top Group 1", hash, True), results)
</del><ins>+ group = yield txn.groupByUID(uid)
+ group = yield txn.groupByID(group.groupID)
+ self.assertEqual(group.groupUID, uid)
+ self.assertEqual(group.name, u"Top Group 1")
+ self.assertEqual(group.membershipHash, hash)
+ self.assertEqual(group.extant, True)
</ins><span class="cx">
</span><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="lines">@@ -683,31 +677,25 @@
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- (
- _ignore_groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = (yield txn.groupByUID(uid))
</del><ins>+ group = yield txn.groupByUID(uid)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertTrue(extant)
</del><ins>+ self.assertTrue(group.extant)
</ins><span class="cx">
</span><span class="cx"> # Remove the group
</span><span class="cx"> yield self.directory.removeRecords([uid])
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = (yield txn.groupByUID(uid))
</del><ins>+ group = (yield txn.groupByUID(uid))
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> # Extant = False
</span><del>- self.assertFalse(extant)
</del><ins>+ self.assertFalse(group.extant)
</ins><span class="cx">
</span><span class="cx"> # The list of members stored in the DB for this group is now empty
</span><span class="cx"> txn = store.newTransaction()
</span><del>- members = yield txn.groupMemberUIDs(groupID)
</del><ins>+ members = yield txn.groupMemberUIDs(group.groupID)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertEquals(members, set())
</span><span class="cx">
</span><span class="lines">@@ -732,18 +720,15 @@
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = (yield txn.groupByUID(uid))
</del><ins>+ group = (yield txn.groupByUID(uid))
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><span class="cx"> # Extant = True
</span><del>- self.assertTrue(extant)
</del><ins>+ self.assertTrue(group.extant)
</ins><span class="cx">
</span><span class="cx"> # The list of members stored in the DB for this group has 100 users
</span><span class="cx"> txn = store.newTransaction()
</span><del>- members = yield txn.groupMemberUIDs(groupID)
</del><ins>+ members = yield txn.groupMemberUIDs(group.groupID)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> self.assertEquals(len(members), 100 if uid == u"testgroup" else 0)
</span><span class="cx">
</span><span class="lines">@@ -760,27 +745,27 @@
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.refreshGroup(txn, uid)
</span><del>- groupID = (yield txn.groupByUID(uid, create=False))[0]
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertNotEqual(groupID, None)
</del><ins>+ self.assertNotEqual(group, None)
</ins><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.update(txn)
</span><del>- groupID = (yield txn.groupByUID(uid, create=False))[0]
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertEqual(groupID, None)
</del><ins>+ self.assertEqual(group, None)
</ins><span class="cx">
</span><span class="cx"> # delegate groups not deleted
</span><span class="cx"> for uid in (u"testgroup", u"emptygroup",):
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><del>- groupID = (yield txn.groupByUID(uid))[0]
- yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=groupID, readWrite=True)
</del><ins>+ group = yield txn.groupByUID(uid)
+ yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertNotEqual(groupID, None)
</del><ins>+ self.assertNotEqual(group, None)
</ins><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.update(txn)
</span><span class="lines">@@ -788,21 +773,21 @@
</span><span class="cx"> yield JobItem.waitEmpty(store.newTransaction, reactor, 60)
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><del>- groupID = (yield txn.groupByUID(uid, create=False))[0]
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertNotEqual(groupID, None)
</del><ins>+ self.assertNotEqual(group, None)
</ins><span class="cx">
</span><span class="cx"> # delegate group is deleted. unused group is deleted
</span><span class="cx"> txn = store.newTransaction()
</span><del>- testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
- yield txn.removeDelegateGroup(delegator=u"sagen", delegateGroupID=testGroupID, readWrite=True)
- testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
- emptyGroupID = (yield txn.groupByUID(u"emptygroup", create=False))[0]
</del><ins>+ testGroup = yield txn.groupByUID(u"testgroup", create=False)
+ yield txn.removeDelegateGroup(delegator=u"sagen", delegateGroupID=testGroup.groupID, readWrite=True)
+ testGroup = yield txn.groupByUID(u"testgroup", create=False)
+ emptyGroup = yield txn.groupByUID(u"emptygroup", create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertNotEqual(testGroupID, None)
- self.assertNotEqual(emptyGroupID, None)
</del><ins>+ self.assertNotEqual(testGroup, None)
+ self.assertNotEqual(emptyGroup, None)
</ins><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.update(txn)
</span><span class="lines">@@ -810,12 +795,12 @@
</span><span class="cx"> yield JobItem.waitEmpty(store.newTransaction, reactor, 60)
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><del>- testGroupID = (yield txn.groupByUID(u"testgroup", create=False))[0]
- emptyGroupID = (yield txn.groupByUID(u"emptygroup", create=False))[0]
</del><ins>+ testGroup = yield txn.groupByUID(u"testgroup", create=False)
+ emptyGroup = yield txn.groupByUID(u"emptygroup", create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertEqual(testGroupID, None)
- self.assertNotEqual(emptyGroupID, None)
</del><ins>+ self.assertEqual(testGroup, None)
+ self.assertNotEqual(emptyGroup, None)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -831,42 +816,33 @@
</span><span class="cx">
</span><span class="cx"> config.AutomaticPurging.GroupPurgeIntervalSeconds = oldGroupPurgeIntervalSeconds
</span><span class="cx"> txn = store.newTransaction()
</span><del>- groupID = (yield txn.groupByUID(uid))[0]
- yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=groupID, readWrite=True)
- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = yield txn.groupByUID(uid, create=False)
</del><ins>+ group = yield txn.groupByUID(uid)
+ yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True)
+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx">
</span><del>- self.assertTrue(extant)
- self.assertNotEqual(groupID, None)
</del><ins>+ self.assertNotEqual(group, None)
+ self.assertTrue(group.extant)
</ins><span class="cx">
</span><span class="cx"> # Remove the group, still cached
</span><span class="cx"> yield self.directory.removeRecords([uid])
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.update(txn)
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = yield txn.groupByUID(uid, create=False)
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><span class="cx"> yield JobItem.waitEmpty(store.newTransaction, reactor, 60)
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><del>- (
- groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
- extant
- ) = yield txn.groupByUID(uid, create=False)
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><del>- self.assertNotEqual(groupID, None)
- self.assertFalse(extant)
</del><ins>+ self.assertNotEqual(group, None)
+ self.assertFalse(group.extant)
</ins><span class="cx">
</span><span class="cx"> # delete the group
</span><span class="cx"> config.AutomaticPurging.GroupPurgeIntervalSeconds = "0.0"
</span><span class="cx">
</span><span class="cx"> txn = store.newTransaction()
</span><span class="cx"> yield self.groupCacher.update(txn)
</span><del>- groupID = (yield txn.groupByUID(uid, create=False))[0]
</del><ins>+ group = yield txn.groupByUID(uid, create=False)
</ins><span class="cx"> yield txn.commit()
</span><del>- self.assertEqual(groupID, None)
</del><ins>+ self.assertEqual(group, None)
</ins></span></pre>
</div>
</div>
</body>
</html>