[CalendarServer-changes] [13512] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon May 19 20:39:30 PDT 2014
Revision: 13512
http://trac.calendarserver.org//changeset/13512
Author: gaya at apple.com
Date: 2014-05-19 20:39:30 -0700 (Mon, 19 May 2014)
Log Message:
-----------
Remove GROUP_ATTENDDEE rows for old events
Modified Paths:
--------------
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/who/test/test_group_attendees.py
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py 2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py 2014-05-20 03:39:30 UTC (rev 13512)
@@ -1041,6 +1041,7 @@
"GroupAttendees" : {
"Enabled": False,
"ReconciliationDelaySeconds" : 5,
+ "UpdateOldEventLimitSeconds" : 1 * 24 * 60 * 60, # 1 day
},
"AutomaticPurging": {
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py 2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py 2014-05-20 03:39:30 UTC (rev 13512)
@@ -1964,8 +1964,7 @@
changed = component.reconcileGroupAttendees(groupCUAToAttendeeMemberPropMap)
# save for post processing when self._resourceID is non-zero
- if inserting and groupCUAToAttendeeMemberPropMap:
- self._groupCUAToAttendeeMemberPropMap = groupCUAToAttendeeMemberPropMap
+ self._groupCUAToAttendeeMemberPropMap = groupCUAToAttendeeMemberPropMap
returnValue(changed)
@@ -2037,17 +2036,50 @@
@inlineCallbacks
- def deleteGROUP_ATTENDEE(self, groupCUAToAttendeeMemberPropMap=None):
- """
- delete groupp attendee rows for this resource
- """
- ga = schema.GROUP_ATTENDEE
- rows = yield Delete(
- From=ga,
- Where=ga.RESOURCE_ID == self._resourceID,
- Return=[ga.GROUP_ID]
- ).on(self._txn)
- returnValue(bool(rows))
+ def deleteOldGROUP_ATTENDEE(self, component, instances, inserting, txn):
+
+ # If this event is old, break possible tie to group update
+ if hasattr(self, "_groupCUAToAttendeeMemberPropMap"):
+
+ if (component.masterComponent() is None or not component.isRecurring()):
+ cutoffDate_datatime = (
+ datetime.datetime.utcnow() +
+ datetime.timedelta(seconds=config.GroupAttendees.UpdateOldEventLimitSeconds)
+ )
+ tr = schema.TIME_RANGE
+ rows = yield Select(
+ [Count(tr.CALENDAR_OBJECT_RESOURCE_ID)],
+ From=tr,
+ Where=(
+ tr.CALENDAR_OBJECT_RESOURCE_ID == self._resourceID).And(
+ tr.END_DATE > cutoffDate_datatime
+ ),
+ ).on(txn)
+ isOld = rows[0][0] == 0
+
+ else:
+ if instances:
+ cutoffDate_DateTime = (
+ DateTime.getNowUTC() +
+ Duration(seconds=config.GroupAttendees.UpdateOldEventLimitSeconds)
+ )
+ maxInstanceKey = sorted(instance for instance in instances)[-1]
+ isOld = cutoffDate_DateTime > instances[maxInstanceKey].end
+ else:
+ isOld = True
+
+ if isOld:
+ if inserting:
+ # don't create GROUP_ATTENDEE rows in updateGROUP_ATTENDEE()
+ del self._groupCUAToAttendeeMemberPropMap
+ else:
+ # delete existing group rows
+ ga = schema.GROUP_ATTENDEE
+ rows = yield Delete(
+ From=ga,
+ Where=ga.RESOURCE_ID == self._resourceID,
+ #Return=[ga.GROUP_ID]
+ ).on(txn)
def validCalendarDataCheck(self, component, inserting):
@@ -2742,7 +2774,8 @@
yield self.updateDatabase(component, inserting=inserting)
# update GROUP_ATTENDEE table rows
- yield self.updateGROUP_ATTENDEE()
+ if inserting:
+ yield self.updateGROUP_ATTENDEE()
# Post process managed attachments
if internal_state in (
@@ -2806,6 +2839,7 @@
# freebusy related properties have changed (e.g. an attendee reply and refresh). In those cases
# the component will have a special attribute present to let us know to suppress the instance indexing.
instanceIndexingRequired = not getattr(component, "noInstanceIndexing", False) or inserting or reCreate
+ instances = None
if instanceIndexingRequired:
@@ -2879,7 +2913,7 @@
# Now coerce indexing to off if needed
if not doInstanceIndexing:
- instances = None
+ #instances = None # used by deleteOldGROUP_ATTENDEE() call at end
recurrenceLowerLimit = None
recurrenceLimit = DateTime(1900, 1, 1, 0, 0, 0, tzid=Timezone(utc=True))
@@ -2988,6 +3022,8 @@
if instanceIndexingRequired and doInstanceIndexing:
yield self._addInstances(component, instances, truncateLowerLimit, isInboxItem, txn)
+
+ yield self.deleteOldGROUP_ATTENDEE(component, instances, inserting, txn)
@inlineCallbacks
Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py 2014-05-20 01:09:59 UTC (rev 13511)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py 2014-05-20 03:39:30 UTC (rev 13512)
@@ -58,6 +58,7 @@
super(GroupAttendeeReconciliation, self).configure()
config.GroupAttendees.Enabled = True
config.GroupAttendees.ReconciliationDelaySeconds = 0
+ config.GroupAttendees.UpdateOldEventLimitSeconds = 0
@inlineCallbacks
@@ -670,6 +671,64 @@
@inlineCallbacks
+ def test_groupPutOldEvent(self):
+ """
+ Test that old event with group attendee is expaned but not linked to group update
+ """
+
+ 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 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;MEMBER="urn:x-uid:20000000-0000-0000-0000-000000000001";PARTSTAT=NEEDS-ACTION;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:x-uid:10000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+ groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+
+ calendar = yield self.calendarUnderTest(name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar = Component.fromString(data_put_1)
+ yield calendar.createCalendarObjectWithName("data1.ics", vcalendar)
+ yield self.commit()
+
+ wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+ if len(wps): # This is needed because the test currently fails and does actually create job items we have to wait for
+ yield self.commit()
+ yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+ self.assertEqual(len(wps), 0)
+
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar = yield cobj.component()
+ self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_1))
+
+
+ @inlineCallbacks
def test_groupChangeOldEvent(self):
"""
Test that group attendee changes not applied to old events
@@ -784,10 +843,139 @@
yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
self.assertEqual(len(wps), 0)
- test_groupChangeOldEvent.todo = "Doesn't work yet"
+ @inlineCallbacks
+ def test_groupChangeOldNoMasterEvent(self):
+ """
+ Test that group attendee changes not applied to old events with no master event
+ """
+ yield None
+ test_groupChangeOldNoMasterEvent.todo = "Create test data"
+
+
@inlineCallbacks
+ def test_groupChangeOldRecurringEvent(self):
+ """
+ Test that group attendee changes not applied to old recurring events
+ """
+
+ 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:20120101T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;UNTIL=20240101T100000
+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_put_2 = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20120101T100000
+DURATION:PT1H
+RRULE:FREQ=DAILY;UNTIL=20140101T100000
+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:20120101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+ATTENDEE;CN=User 01;EMAIL=user01 at example.com;MEMBER="urn:x-uid:20000000-0000-0000-0000-000000000001";PARTSTAT=NEEDS-ACTION;RSVP=TRUE;SCHEDULE-STATUS=1.2:urn:x-uid:10000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+RRULE:FREQ=DAILY;UNTIL=20240101T100000
+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:20120101T100000
+DURATION:PT1H
+ATTENDEE;CN=User 02;EMAIL=user02 at example.com;RSVP=TRUE:urn:x-uid:10000000-0000-0000-0000-000000000002
+ATTENDEE;CN=Group 01;CUTYPE=GROUP;EMAIL=group01 at example.com;RSVP=TRUE;SCHEDULE-STATUS=3.7:urn:x-uid:20000000-0000-0000-0000-000000000001
+CREATED:20060101T150000Z
+ORGANIZER;CN=User 02;EMAIL=user02 at example.com:urn:x-uid:10000000-0000-0000-0000-000000000002
+RRULE:FREQ=DAILY;UNTIL=20140101T100000
+SEQUENCE:1
+SUMMARY:event 1
+END:VEVENT
+END:VCALENDAR
+"""
+
+ @inlineCallbacks
+ def expandedMembers(self, records=None):
+ yield None
+ returnValue(set())
+
+ groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+
+ calendar = yield self.calendarUnderTest(name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar = Component.fromString(data_put_1)
+ yield calendar.createCalendarObjectWithName("data1.ics", vcalendar)
+ yield self.commit()
+
+ wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+ yield self.commit()
+ self.assertEqual(len(wps), 1)
+ yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar = yield cobj.component()
+ self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_1))
+
+ yield self._verifyObjectResourceCount("10000000-0000-0000-0000-000000000001", 1)
+
+ vcalendar = Component.fromString(data_put_2)
+ yield cobj.setComponent(vcalendar)
+ yield self.commit()
+
+ self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers)
+
+ cobj = yield self.calendarObjectUnderTest(name="data1.ics", calendar_name="calendar", home="10000000-0000-0000-0000-000000000002")
+ vcalendar = yield cobj.component()
+ self.assertEqual(normalize_iCalStr(vcalendar), normalize_iCalStr(data_get_2))
+
+ wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "20000000-0000-0000-0000-000000000001")
+ if len(wps): # This is needed because the test currently fails and does actually create job items we have to wait for
+ yield self.commit()
+ yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+ self.assertEqual(len(wps), 0)
+
+
+
+ @inlineCallbacks
def test_groupRemovalFromDirectory(self):
"""
Test that removing a group from the directory also removes the expanded attendees.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140519/95492704/attachment-0001.html>
More information about the calendarserver-changes
mailing list