[CalendarServer-changes] [13304] CalendarServer/trunk/txdav
source_changes at macosforge.org
source_changes at macosforge.org
Wed Apr 16 17:26:27 PDT 2014
Revision: 13304
http://trac.calendarserver.org//changeset/13304
Author: gaya at apple.com
Date: 2014-04-16 17:26:27 -0700 (Wed, 16 Apr 2014)
Log Message:
-----------
update group attendees after group change
Modified Paths:
--------------
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/who/groups.py
CalendarServer/trunk/txdav/who/test/test_group_attendees.py
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2014-04-16 20:47:22 UTC (rev 13303)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2014-04-17 00:26:27 UTC (rev 13304)
@@ -1915,7 +1915,7 @@
yield component.normalizeCalendarUserAddresses(normalizationLookup, self.directoryService().recordWithCalendarUserAddress)
# Expand groups
- yield self.expandGroupAttendees(component)
+ yield self.expandGroupAttendees(component, inserting)
# Valid attendee list size check
yield self.validAttendeeListSizeCheck(component, inserting)
@@ -1933,12 +1933,12 @@
@inlineCallbacks
- def expandGroupAttendees(self, component):
+ def expandGroupAttendees(self, component, inserting):
"""
Expand group attendees
"""
if not config.Scheduling.Options.AllowGroupAsAttendee:
- return
+ returnValue(None)
attendeeProps = component.getAllAttendeeProperties()
groupCUAs = set([
@@ -1951,33 +1951,35 @@
if groupRecord:
members = yield groupRecord.expandedMembers()
- # calculate hash
- memberUIDs = sorted([member.uid for member in members])
- membershipHashContent = hashlib.md5()
- for memberUID in memberUIDs:
- membershipHashContent.update(memberUID)
- membershipHash = membershipHashContent.hexdigest()
-
- # associate group ID with self
- groupID, _ignore_name, membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupRecord.uid)
- try:
- groupAttendee = schema.GROUP_ATTENDEE
- yield Insert({
- groupAttendee.RESOURCE_ID: self._resourceID,
- groupAttendee.GROUP_ID: groupID,
- groupAttendee.MEMBERSHIP_HASH: membershipHash,
- })
- except AllRetriesFailed:
- pass
-
- # get members
+ # add group attendees
yield component.expandGroupAttendee(
groupRecord.canonicalCalendarUserAddress(),
set([member.canonicalCalendarUserAddress() for member in members]),
self.directoryService().recordWithCalendarUserAddress
)
+ # tie event to group cacher
+ if not inserting:
+ # calculate hash
+ memberUIDs = sorted([member.uid for member in members])
+ membershipHashContent = hashlib.md5()
+ for memberUID in memberUIDs:
+ membershipHashContent.update(memberUID)
+ membershipHash = membershipHashContent.hexdigest()
+ # associate group ID with self
+ groupID, _ignore_name, membershipHash, _ignore_modDate = yield self._txn.groupByUID(groupRecord.uid)
+ try:
+ groupAttendee = schema.GROUP_ATTENDEE
+ yield Insert({
+ groupAttendee.RESOURCE_ID: self._resourceID,
+ groupAttendee.GROUP_ID: groupID,
+ groupAttendee.MEMBERSHIP_HASH: membershipHash,
+ }).on(self._txn)
+ except AllRetriesFailed:
+ pass
+
+
def validCalendarDataCheck(self, component, inserting):
"""
Check that the calendar data is valid iCalendar.
@@ -2632,6 +2634,10 @@
yield self.updateDatabase(component, inserting=inserting)
+ # add GROUP_ATTENNDEE rows using just created _resourceID
+ if inserting:
+ yield self.expandGroupAttendees(component, False)
+
# Post process managed attachments
if internal_state in (
ComponentUpdateState.NORMAL,
Modified: CalendarServer/trunk/txdav/who/groups.py
===================================================================
--- CalendarServer/trunk/txdav/who/groups.py 2014-04-16 20:47:22 UTC (rev 13303)
+++ CalendarServer/trunk/txdav/who/groups.py 2014-04-17 00:26:27 UTC (rev 13304)
@@ -24,6 +24,7 @@
from twext.enterprise.jobqueue import WorkItem, PeerConnectionPool
from twisted.internet.defer import inlineCallbacks, returnValue
from txdav.common.datastore.sql_tables import schema
+from txdav.caldav.datastore.sql import Calendar
import datetime
import hashlib
@@ -160,7 +161,7 @@
):
group = property(
- lambda self: "{0}, {1}".format(self.groupID, self.eventID)
+ lambda self: "{0}, {1}".format(self.groupID, self.resourceID)
)
@inlineCallbacks
@@ -169,39 +170,46 @@
# Delete all other work items for this group
yield Delete(
From=self.table,
- Where=((self.table.GROUP_ID == self.self.groupID).And(
- self.table.RESOURCE_ID == self.self.eventID)
+ Where=((self.table.GROUP_ID == self.groupID).And(
+ self.table.RESOURCE_ID == self.resourceID)
)
).on(self.transaction)
- # get calendar Object
- calObject = schema.CALENDAR_OBJECT
+ # get calendar id
+ co = schema.CALENDAR_OBJECT
rows = yield Select(
- [calObject.CALENDAR_RESOURCE_ID, ],
- From=calObject,
- Where=calObject.RESOURCE_ID == self.eventID,
+ [co.CALENDAR_RESOURCE_ID, ],
+ From=co,
+ Where=co.RESOURCE_ID == self.resourceID,
).on(self.transaction)
+ calendarID = rows[0][0]
- calendarID = rows[0][0]
- calendarHome = (yield self.Calendar._ownerHomeWithResourceID.on(
+ # get home id
+ calendarHomeID = (yield Calendar._ownerHomeWithResourceID.on(
self.transaction, resourceID=calendarID)
)[0][0]
+ # get db objects
+ calendarHome = yield self.transaction.calendarHomeWithResourceID(calendarHomeID)
calendar = yield calendarHome.childWithID(calendarID)
- calendarObject = yield calendar.objectResourceWithID(self.eventID)
+ calendarObject = yield calendar.objectResourceWithID(self.resourceID)
+ component = yield calendarObject.component()
- # get group individual UIDs
- groupMemember = schema.GROUP_MEMBERSHIP
- rows = yield Select(
- [groupMemember.MEMBER_GUID, ],
- From=groupMemember,
- Where=groupMemember.GROUP_ID == self.groupID,
- ).on(self.transaction)
- memberGUIDs = [row[0] for row in rows]
+ # TODO: Check performance because:
+ # 1) if the component is changed then expandGroupAttendee() will be called again to validate
+ # 2) The group and members are in the group cache so could use them here
- component = yield calendarObject.component()
- changed = component.expandGroupAttendee(self.groupGUID, memberGUIDs, self.directoryService().recordWithCalendarUserAddress)
+ # get group record and members
+ groupUID, _ignore_name, _ignore_membershipHash = yield self.transaction.groupByID(self.groupID)
+ groupRecord = yield self.transaction.directoryService().recordWithUID(groupUID)
+ members = yield groupRecord.expandedMembers() if groupRecord else set()
+ # expand
+ changed = yield component.expandGroupAttendee(
+ groupRecord.canonicalCalendarUserAddress(),
+ set([member.canonicalCalendarUserAddress() for member in members]),
+ self.transaction.directoryService().recordWithCalendarUserAddress
+ )
if changed:
yield calendarObject.setComponent(component)
@@ -319,9 +327,12 @@
@inlineCallbacks
def refreshGroup(self, txn, groupUID):
- # Does the work of a per-group refresh work item
- # Faults in the flattened membership of a group, as UIDs
- # and updates the GROUP_MEMBERSHIP table
+ """
+ Does the work of a per-group refresh work item
+ Faults in the flattened membership of a group, as UIDs
+ and updates the GROUP_MEMBERSHIP table
+ WorkProposals are returned for tests
+ """
self.log.debug("Faulting in group: {g}", g=groupUID)
record = (yield self.directory.recordWithUID(groupUID))
if record is None:
@@ -357,9 +368,11 @@
newMemberUIDs.add(member.uid)
yield self.synchronizeMembers(txn, groupID, newMemberUIDs)
- yield self.scheduleEventReconciliations(txn, groupID, groupUID)
+ wps = yield self.scheduleEventReconciliations(txn, groupID)
+ returnValue(wps)
+
@inlineCallbacks
def synchronizeMembers(self, txn, groupID, newMemberUIDs):
numRemoved = numAdded = 0
@@ -400,10 +413,11 @@
@inlineCallbacks
- def scheduleEventReconciliations(self, txn, groupID, groupUID):
+ def scheduleEventReconciliations(self, txn, groupID):
"""
Find all events who have this groupID as an attendee and create
work items for them.
+ returns: WorkProposal list
"""
groupAttendee = schema.GROUP_ATTENDEE
rows = yield Select(
@@ -413,6 +427,7 @@
).on(txn)
eventIDs = [row[0] for row in rows]
+ wps = []
for eventID in eventIDs:
notBefore = (
@@ -421,21 +436,23 @@
)
log.debug(
"scheduling group reconciliation for "
- "({eventID}, {groupID}, {groupUID}): {when}",
- eventID=eventID,
+ "({resourceID}, {groupID},): {when}",
+ resourceID=eventID,
groupID=groupID,
- groupUID=groupUID,
- when=notBefore)
+ when=notBefore
+ )
- yield txn.enqueue(
+ wp = yield txn.enqueue(
GroupAttendeeReconciliationWork,
- eventID=eventID,
+ resourceID=eventID,
groupID=groupID,
- groupUID=groupUID,
notBefore=notBefore
)
+ wps.append(wp)
+ returnValue(tuple(wps))
+
@inlineCallbacks
def groupsToRefresh(self, txn):
delegatedUIDs = set((yield txn.allGroupDelegates()))
Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py 2014-04-16 20:47:22 UTC (rev 13303)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py 2014-04-17 00:26:27 UTC (rev 13304)
@@ -19,11 +19,13 @@
"""
from twext.who.test.test_xml import xmlService
-from twisted.internet.defer import inlineCallbacks
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.trial import unittest
from twistedcaldav.config import config
from twistedcaldav.ical import Component, normalize_iCalStr
from txdav.caldav.datastore.test.util import buildCalendarStore, populateCalendarsFrom, CommonCommonTests
+from txdav.who.directory import CalendarDirectoryRecordMixin
+from txdav.who.groups import GroupCacher
from txdav.who.util import directoryFromConfig
import os
@@ -71,6 +73,9 @@
"10000000-0000-0000-0000-000000000001" : {
"calendar" : {}
},
+ "10000000-0000-0000-0000-000000000002" : {
+ "calendar" : {}
+ },
}
@inlineCallbacks
@@ -337,3 +342,115 @@
cobj1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000001")
vcalendar2 = yield cobj1.component()
self.assertEqual(normalize_iCalStr(vcalendar2), normalize_iCalStr(data_get_1))
+
+
+ @inlineCallbacks
+ def test_groupChange(self):
+ """
+ Test that group attendee is expanded on PUT
+ """
+ data_put_1 = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+SUMMARY:event 1
+UID:event1 at ninevah.local
+ORGANIZER:MAILTO:user02 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:MAILTO:group01 at example.com
+END:VEVENT
+END:VCALENDAR"""
+
+ data_get_1 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;RSVP=TRUE:urn:uuid:10000000-0000-0000-0000-000000000001
+ATTENDEE;CN=Group 02;CUTYPE=GROUP;EMAIL=group02 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:uuid:20000000-0000-0000-0000-000000000002
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 01;EMAIL=user01 at example.com:urn:uuid:10000000-0000-0000-0000-000000000001
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_get_2 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:uuid:20000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:uuid:10000000-0000-0000-0000-000000000002
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ data_get_3 = """BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+BEGIN:VEVENT
+UID:event1 at ninevah.local
+DTSTART;TZID=US/Eastern:20140101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:uuid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:uuid:20000000-0000-0000-0000-000000000001
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;MEMBER="urn:uuid:20000000-0000-0000-0000-000000000001";PARTSTAT=NEEDS-ACTION:urn:uuid:10000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:uuid:10000000-0000-0000-0000-000000000002
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ #TODO: should User 01 have SCHEDULE-STATUS=3.7 ?
+
+
+ @inlineCallbacks
+ def expandedMembers(self, records=None):
+ yield None
+ returnValue(set())
+
+ unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers
+ self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers)
+
+ groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+ wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+ self.assertEqual(set(wps), set())
+
+ calendar = yield self.calendarUnderTest(name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar1 = Component.fromString(data_put_1)
+ yield calendar.createCalendarObjectWithName("data1.ics", vcalendar1)
+ yield self.commit()
+
+ cobj1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar2 = yield cobj1.component()
+ self.assertEqual(normalize_iCalStr(vcalendar2), normalize_iCalStr(data_get_2))
+
+ self.patch(CalendarDirectoryRecordMixin, "expandedMembers", unpatchedExpandedMembers)
+
+ groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+ wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+ yield self.commit()
+ self.assertEqual(len(wps), 1)
+ for wp in wps:
+ yield wp.whenExecuted()
+
+ cobj1 = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar3 = yield cobj1.component()
+ self.assertEqual(normalize_iCalStr(vcalendar3), normalize_iCalStr(data_get_3))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140416/1e454591/attachment-0001.html>
More information about the calendarserver-changes
mailing list