def _runAllJobs(self): """ Run all outstanding jobs. """ # Run jobs jobs = yield JobItem.all(self.transactionUnderTest()) while jobs: yield jobs[0].run() yield self.commit() jobs = yield JobItem.all(self.transactionUnderTest()) yield self.commit()
def test_cascade_delete_cleanup(self): """ Test that when work associated with L{txdav.caldav.datastore.scheduling.work.ScheduleWork} is removed with the L{ScheduleWork} item being removed, the associated L{JobItem} runs and removes itself and the L{ScheduleWork}. """ ScheduleWorkMixin._queued = 0 txn = self.transactionUnderTest() home = yield self.homeUnderTest(name="user01") yield ScheduleOrganizerWork.schedule( txn, "12345-67890", "create", home, None, None, self.calendar_new, "urn:uuid:user01", 2, True, ) yield self.commit() self.assertEqual(ScheduleWorkMixin._queued, 1) jobs = yield JobItem.all(self.transactionUnderTest()) work = yield jobs[0].workItem() yield WorkItem.delete(work) yield self.commit() jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 1) baseWork = yield ScheduleWork.all(self.transactionUnderTest()) self.assertEqual(len(baseWork), 1) self.assertEqual(baseWork[0].jobID, jobs[0].jobID) work = yield jobs[0].workItem() self.assertTrue(work is None) yield self.commit() yield JobItem.waitEmpty(self.storeUnderTest().newTransaction, reactor, 60) jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 0) work = yield ScheduleOrganizerWork.all(self.transactionUnderTest()) self.assertEqual(len(work), 0) baseWork = yield ScheduleWork.all(self.transactionUnderTest()) self.assertEqual(len(baseWork), 0)
def test_create(self): """ Test that jobs associated with L{txdav.caldav.datastore.scheduling.work.ScheduleOrganizerSendWork} can be created and correctly removed. """ txn = self.transactionUnderTest() home = yield self.homeUnderTest(name="user01") yield ScheduleOrganizerSendWork.schedule( txn, "create", home, None, "urn:x-uid:user01", "urn:x-uid:user02", self.itip_new, True, 1000, ) jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 1) work = yield jobs[0].workItem() yield work.doWork() home2 = yield self.calendarUnderTest(home="user02", name="calendar") cobjs = yield home2.calendarObjects() self.assertEqual(len(cobjs), 1) # cal2 = yield cobjs[0].component() yield work.delete() yield jobs[0].delete() yield self.commit()
def stopIt(): txn = store.newTransaction() jobs = yield JobItem.all(txn) yield txn.commit() if enableJobProcessing: yield pool.stopService() # active transactions should have been shut down. wasBusy = len(cp._busy) busyText = repr(cp._busy) result = yield cp.stopService() if deriveValue(testCase, _SPECIAL_TXN_CLEAN, lambda tc: False): if wasBusy: testCase.fail("Outstanding Transactions: " + busyText) returnValue(result) if len(jobs): testCase.fail("Jobs left in job queue {}: {}".format( testCase, ",".join([job.workType for job in jobs]) )) returnValue(result)
def test_create(self): """ Test that jobs associated with L{txdav.caldav.datastore.scheduling.work.ScheduleOrganizerSendWork} can be created and correctly removed. """ txn = self.transactionUnderTest() home = yield self.homeUnderTest(name="user01") yield ScheduleOrganizerSendWork.schedule( txn, "create", home, None, "urn:x-uid:user01", "urn:x-uid:user02", self.itip_new, True, 1000, ) jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 1) work = yield jobs[0].workItem() yield work.doWork() home2 = yield self.calendarUnderTest(home="user02", name="calendar") cobjs = yield home2.calendarObjects() self.assertEqual(len(cobjs), 1) #cal2 = yield cobjs[0].component() yield work.delete() yield jobs[0].delete() yield self.commit()
def _runOneJob(self): """ Run the first outstanding jobs. """ # Run jobs jobs = yield JobItem.all(self.transactionUnderTest()) for job in jobs: yield job.run() break yield self.commit()
def test_create(self): """ Test that jobs associated with L{txdav.caldav.datastore.scheduling.work.ScheduleOrganizerWork} can be created and correctly removed. """ ScheduleWorkMixin._queued = 0 txn = self.transactionUnderTest() home = yield self.homeUnderTest(name="user01") yield ScheduleOrganizerWork.schedule( txn, "12345-67890", "create", home, None, None, self.calendar_new, "urn:uuid:user01", 2, True, ) yield self.commit() self.assertEqual(ScheduleWorkMixin._queued, 1) jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 1) work = yield jobs[0].workItem() self.assertTrue(isinstance(work, ScheduleOrganizerWork)) self.assertEqual(work.icalendarUid, "12345-67890") self.assertEqual(scheduleActionFromSQL[work.scheduleAction], "create") yield work.delete() yield jobs[0].delete() yield self.commit() jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 0) work = yield ScheduleOrganizerWork.all(self.transactionUnderTest()) self.assertEqual(len(work), 0) baseWork = yield ScheduleWork.all(self.transactionUnderTest()) self.assertEqual(len(baseWork), 0)
def stopIt(): txn = store.newTransaction() jobs = yield JobItem.all(txn) yield txn.commit() if len(jobs): print("Jobs left in job queue {}: {}".format( testCase, ",".join([job.workType for job in jobs]) )) if enableJobProcessing: yield pool.stopService() # active transactions should have been shut down. wasBusy = len(cp._busy) busyText = repr(cp._busy) result = yield cp.stopService() if deriveValue(testCase, _SPECIAL_TXN_CLEAN, lambda tc: False): if wasBusy: testCase.fail("Outstanding Transactions: " + busyText) returnValue(result) returnValue(result)
def test_replyBeforeOrganizerConsequentialChange(self): """ Test that the organizer and attendee see the attendee's partstat change when the organizer makes a consequential change whilst the attendee reply is in progress. """ organizer1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION:urn:x-uid:user02 END:VEVENT END:VCALENDAR """) organizer2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080602T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION:urn:x-uid:user02 SUMMARY:Test END:VEVENT END:VCALENDAR """) organizer3 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080602T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE;SCHEDULE-STATUS=1.2;X-CALENDARSERVER-RESET-PARTSTAT=1:urn:x-uid:user02 SEQUENCE:1 SUMMARY:Test END:VEVENT END:VCALENDAR """) attendee1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) attendee2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user02 END:VEVENT END:VCALENDAR """) attendee3 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080602T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected];SCHEDULE-STATUS=1.2:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 SEQUENCE:1 SUMMARY:Test TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) yield self.createOrganizerEvent("user01", organizer1) attendee = yield self.getAttendeeEvent("user02") self.assertEqual(attendee, attendee1, msg=diff_iCalStrs(attendee, attendee1)) yield self.setOrganizerEvent("user01", organizer2, run_jobs=False) yield self._runOneJob() yield self.setAttendeeEvent("user02", attendee2, run_jobs=False) yield self._runAllJobs() jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 0) yield self.commit() organizer = yield self.getOrganizerEvent("user01") self.assertEqual(normalize_iCalStr(organizer), normalize_iCalStr(organizer3), msg=diff_iCalStrs(organizer3, organizer)) attendee = yield self.getAttendeeEvent("user02") self.assertEqual(normalize_iCalStr(attendee), normalize_iCalStr(attendee3), msg=diff_iCalStrs(attendee3, attendee))
def test_replyBeforeOrganizerEXDATE(self): """ Test that a reply is sent if an attendee changes an event, but the organizer exdate's the instance before the reply work is processed. """ organizer1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION:urn:x-uid:user02 RRULE:FREQ=DAILY END:VEVENT END:VCALENDAR """) attendee1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 RRULE:FREQ=DAILY TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) organizer2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION:urn:x-uid:user02 EXDATE:20080602T130000Z RRULE:FREQ=DAILY SUMMARY:Test END:VEVENT END:VCALENDAR """) attendee2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 RRULE:FREQ=DAILY TRANSP:TRANSPARENT END:VEVENT BEGIN:VEVENT UID:12345-67890 RECURRENCE-ID:20080602T130000Z DTSTAMP:20080601T130000Z DTSTART:20080602T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=DECLINED:urn:x-uid:user02 TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) attendee3 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 EXDATE:20080602T130000Z RRULE:FREQ=DAILY TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) yield self.createOrganizerEvent("user01", organizer1) attendee = yield self.getAttendeeEvent("user02") self.assertEqual(attendee, attendee1, msg=diff_iCalStrs(attendee, attendee1)) yield self.setOrganizerEvent("user01", organizer2, run_jobs=False) yield self._runOneJob() yield self.setAttendeeEvent("user02", attendee2, run_jobs=False) yield self.setAttendeeEvent("user02", attendee3, run_jobs=False) yield self._runAllJobs() jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 0) yield self.commit()
def test_replyBeforeResourceDelete(self): """ Test that a reply is sent if an attendee changes an event, then immediately deletes it. """ organizer1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION:urn:x-uid:user02 END:VEVENT END:VCALENDAR """) attendee1 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=NEEDS-ACTION;RSVP=TRUE:urn:x-uid:user02 TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) organizer2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=DECLINED;SCHEDULE-STATUS=2.0:urn:x-uid:user02 END:VEVENT END:VCALENDAR """) attendee2 = Component.fromString("""BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:12345-67890 DTSTAMP:20080601T130000Z DTSTART:20080601T130000Z DURATION:PT1H ORGANIZER;CN=User 01;[email protected]:urn:x-uid:user01 ATTENDEE;CN=User 01;[email protected];PARTSTAT=ACCEPTED:urn:x-uid:user01 ATTENDEE;CN=User 02;[email protected];PARTSTAT=DECLINED:urn:x-uid:user02 TRANSP:TRANSPARENT END:VEVENT END:VCALENDAR """) yield self.createOrganizerEvent("user01", organizer1) attendee = yield self.getAttendeeEvent("user02") self.assertEqual(attendee, attendee1, msg=diff_iCalStrs(attendee, attendee1)) yield self.setAttendeeEvent("user02", attendee2, run_jobs=False) calobj = yield self.getAttendeeResource("user02") yield calobj.remove() yield self.commit() yield self._runAllJobs() jobs = yield JobItem.all(self.transactionUnderTest()) self.assertEqual(len(jobs), 0) yield self.commit() organizer = yield self.getOrganizerEvent("user01") self.assertEqual(organizer, organizer2, msg=diff_iCalStrs(organizer, organizer2))
def test_upgrade_SCHEDULE_REPLY(self): cal1 = """BEGIN:VCALENDAR VERSION:2.0 PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:1234-5678 DTSTART:20071114T010000Z DURATION:PT1H DTSTAMP:20071114T000000Z ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ORGANIZER:mailto:[email protected] SUMMARY:Test END:VEVENT END:VCALENDAR """ # Load old schema and populate with data schema = yield self._loadOldSchema(self.upgradePath.child("v49.sql")) txn = self.store.newTransaction("loadData") yield Insert({ schema.CALENDAR_HOME.RESOURCE_ID: 1, schema.CALENDAR_HOME.OWNER_UID: "abcdefg", }).on(txn) yield Insert({ schema.CALENDAR.RESOURCE_ID: 2, }).on(txn) yield Insert({ schema.CALENDAR_OBJECT.RESOURCE_ID: 3, schema.CALENDAR_OBJECT.CALENDAR_RESOURCE_ID: 2, schema.CALENDAR_OBJECT.RESOURCE_NAME: "1.ics", schema.CALENDAR_OBJECT.ICALENDAR_TEXT: cal1, schema.CALENDAR_OBJECT.ICALENDAR_UID: "1234-5678", schema.CALENDAR_OBJECT.ICALENDAR_TYPE: "VEVENT", schema.CALENDAR_OBJECT.MD5: "md5-1234567890", }).on(txn) yield Insert({ schema.JOB.JOB_ID: 1, schema.JOB.WORK_TYPE: "SCHEDULE_REPLY_WORK", schema.JOB.NOT_BEFORE: datetime.utcnow(), }).on(txn) yield Insert({ schema.SCHEDULE_WORK.WORK_ID: 1, schema.SCHEDULE_WORK.JOB_ID: 1, schema.SCHEDULE_WORK.ICALENDAR_UID: "1234-5678", schema.SCHEDULE_WORK.WORK_TYPE: "SCHEDULE_REPLY_WORK", }).on(txn) yield Insert({ schema.SCHEDULE_REPLY_WORK.WORK_ID: 1, schema.SCHEDULE_REPLY_WORK.HOME_RESOURCE_ID: 1, schema.SCHEDULE_REPLY_WORK.RESOURCE_ID: 3, schema.SCHEDULE_REPLY_WORK.CHANGED_RIDS: None, }).on(txn) yield txn.commit() # Try to upgrade and verify new version afterwards upgrader = UpgradeDatabaseSchemaStep(self.store) yield upgrader.databaseUpgrade() new_version = yield self._loadVersion() self.assertEqual(new_version, self.currentVersion) txn = self.store.newTransaction("loadData") jobs = yield Select(From=schema.JOB, ).on(txn) schedules = yield Select(From=schema.SCHEDULE_WORK, ).on(txn) replies = yield Select(From=schema.SCHEDULE_REPLY_WORK, ).on(txn) self.assertEqual(len(jobs), 1) self.assertEqual(len(schedules), 1) self.assertEqual(len(replies), 1) self.assertEqual(replies[0], [ 1, 1, 3, None, ]) jobs = yield JobItem.all(txn) self.assertEqual(len(jobs), 1) work = yield jobs[0].workItem() self.assertTrue(isinstance(work, ScheduleReplyWork)) workers = yield ScheduleWork.all(txn) self.assertEqual(len(workers), 1) self.assertEqual(workers[0].workType, "SCHEDULE_REPLY_WORK") yield txn.commit()
def poll(self): if self._polling: return self._polling = True txn = self._store.newTransaction() try: # Look up all of the jobs events = [] jobsByTypeName = {} for job in (yield JobItem.all(txn)): jobsByTypeName.setdefault(job.workType, []).append(job) totalsByTypeName = {} for workType in JobItem.workTypes(): typeName = workType.table.model.name jobs = jobsByTypeName.get(typeName, []) totalsByTypeName[typeName] = len(jobs) jobDicts = [] for job in jobs: def formatTime(datetime): if datetime is None: return None else: # FIXME: Use HTTP time format return datetime.ctime() jobDict = dict( job_jobID=job.jobID, job_priority=job.priority, job_weight=job.weight, job_notBefore=formatTime(job.notBefore), ) work = yield job.workItem() attrs = ("workID", "group") if workType == PushNotificationWork: attrs += ("pushID", "priority") elif workType == ScheduleOrganizerWork: attrs += ("icalendarUid", "attendeeCount") elif workType == ScheduleRefreshWork: attrs += ("icalendarUid", "attendeeCount") elif workType == ScheduleReplyWork: attrs += ("icalendarUid",) elif workType == ScheduleAutoReplyWork: attrs += ("icalendarUid",) elif workType == GroupCacherPollingWork: attrs += () elif workType == IMIPPollingWork: attrs += () elif workType == IMIPReplyWork: attrs += ("organizer", "attendee") else: attrs = () if attrs: if work is None: self.log.error( "workItem() returned None for job: {job}", job=job ) # jobDict.update((attr, None) for attr in attrs) for attr in attrs: jobDict["work_{}".format(attr)] = None else: # jobDict.update( # ("work_{}".format(attr), getattr(work, attr)) # for attr in attrs # ) for attr in attrs: jobDict["work_{}".format(attr)] = ( getattr(work, attr) ) jobDicts.append(jobDict) if jobDicts: events.append(dict( eventClass=typeName, eventID=time(), eventText=asJSON(jobDicts), )) events.append(dict( eventClass=u"work-total", eventID=time(), eventText=asJSON(totalsByTypeName), eventRetry=(self._pollInterval), )) # Send data self.addEvents(events) except: self._polling = False yield txn.abort() raise else: yield txn.commit() # Schedule the next poll if not hasattr(self, "_clock"): from twisted.internet import reactor self._clock = reactor self._clock.callLater(self._pollInterval / 1000, self.poll)
def poll(self): if self._polling: return self._polling = True txn = self._store.newTransaction() try: # Look up all of the jobs events = [] jobsByTypeName = {} for job in (yield JobItem.all(txn)): jobsByTypeName.setdefault(job.workType, []).append(job) totalsByTypeName = {} for workType in JobItem.workTypes(): typeName = workType.table.model.name jobs = jobsByTypeName.get(typeName, []) totalsByTypeName[typeName] = len(jobs) jobDicts = [] for job in jobs: def formatTime(datetime): if datetime is None: return None else: # FIXME: Use HTTP time format return datetime.ctime() jobDict = dict( job_jobID=job.jobID, job_priority=job.priority, job_weight=job.weight, job_notBefore=formatTime(job.notBefore), ) work = yield job.workItem() attrs = ("workID", "group") if workType == PushNotificationWork: attrs += ("pushID", "priority") elif workType == ScheduleOrganizerWork: attrs += ("icalendarUid", "attendeeCount") elif workType == ScheduleRefreshWork: attrs += ("icalendarUid", "attendeeCount") elif workType == ScheduleReplyWork: attrs += ("icalendarUid", ) elif workType == ScheduleAutoReplyWork: attrs += ("icalendarUid", ) elif workType == GroupCacherPollingWork: attrs += () elif workType == IMIPPollingWork: attrs += () elif workType == IMIPReplyWork: attrs += ("organizer", "attendee") else: attrs = () if attrs: if work is None: self.log.error( "workItem() returned None for job: {job}", job=job) # jobDict.update((attr, None) for attr in attrs) for attr in attrs: jobDict["work_{}".format(attr)] = None else: # jobDict.update( # ("work_{}".format(attr), getattr(work, attr)) # for attr in attrs # ) for attr in attrs: jobDict["work_{}".format(attr)] = (getattr( work, attr)) jobDicts.append(jobDict) if jobDicts: events.append( dict( eventClass=typeName, eventID=time(), eventText=asJSON(jobDicts), )) events.append( dict( eventClass=u"work-total", eventID=time(), eventText=asJSON(totalsByTypeName), eventRetry=(self._pollInterval), )) # Send data self.addEvents(events) except: self._polling = False yield txn.abort() raise else: yield txn.commit() # Schedule the next poll if not hasattr(self, "_clock"): from twisted.internet import reactor self._clock = reactor self._clock.callLater(self._pollInterval / 1000, self.poll)
def test_connectionRefusedForAttendee(self): data_organizer = """BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:uid_data DTSTART:{now1:04d}0102T160000Z DURATION:PT1H CREATED:20060102T190000Z DTSTAMP:20051222T210507Z SUMMARY:data01_2 ORGANIZER:mailto:[email protected] ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] END:VEVENT END:VCALENDAR """.replace("\n", "\r\n").format(**self.now) data_attendee = """BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN BEGIN:VEVENT UID:uid_data DTSTART:{now1:04d}0102T160000Z DURATION:PT1H CREATED:20060102T190000Z DTSTAMP:20051222T210507Z SUMMARY:data01_2 ORGANIZER:mailto:[email protected] ATTENDEE:mailto:[email protected] ATTENDEE:mailto:[email protected] ATTENDEE;PARTSTAT=DECLINED:mailto:[email protected] END:VEVENT END:VCALENDAR """.replace("\n", "\r\n").format(**self.now) # Organizer schedules home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user01", create=True) calendar = yield home.childWithName("calendar") yield calendar.createCalendarObjectWithName("1.ics", Component.fromString(data_organizer)) yield self.commitTransaction(0) yield self.waitAllEmpty() # Data for user02 home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(0), name="user02", create=True) calendar = yield home.childWithName("calendar") cobjs = yield calendar.calendarObjects() self.assertEqual(len(cobjs), 1) self.assertEqual(cobjs[0].uid(), "uid_data") yield self.commitTransaction(0) # Data for puser02 home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser02", create=True) calendar = yield home.childWithName("calendar") cobjs = yield calendar.calendarObjects() self.assertEqual(len(cobjs), 1) self.assertEqual(cobjs[0].uid(), "uid_data") yield self.commitTransaction(1) # Stop cross-pod connection from working self.refuseConnection = True # Attendee changes home = yield self.homeUnderTest(txn=self.theTransactionUnderTest(1), name="puser02", create=True) calendar = yield home.childWithName("calendar") cobjs = yield calendar.calendarObjects() yield cobjs[0].setComponent(Component.fromString(data_attendee)) yield self.commitTransaction(1) while True: jobs = yield JobItem.all(self.theTransactionUnderTest(1)) yield self.commitTransaction(1) if len(jobs) == 1 and jobs[0].failed > 0: break # Organizer data unchanged cobj = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") comp = yield cobj.componentForUser() self.assertTrue("DECLINED" not in str(comp)) yield self.commitTransaction(0) # Now allow cross-pod to work self.refuseConnection = False yield self.waitAllEmpty() # Organizer data changed cobj = yield self.calendarObjectUnderTest(txn=self.theTransactionUnderTest(0), home="user01", calendar_name="calendar", name="1.ics") comp = yield cobj.componentForUser() self.assertTrue("DECLINED" in str(comp)) yield self.commitTransaction(0)