class EventSync(BaseSyncMonitor): """Per-account event sync engine.""" def __init__(self, email_address, provider_name, account_id, namespace_id, poll_frequency=300): bind_context(self, 'eventsync', account_id) # Only Google for now, can easily parametrize by provider later. self.provider = GoogleEventsProvider(account_id, namespace_id) BaseSyncMonitor.__init__(self, account_id, namespace_id, email_address, EVENT_SYNC_FOLDER_ID, EVENT_SYNC_FOLDER_NAME, provider_name, poll_frequency=poll_frequency) def sync(self): """Query a remote provider for updates and persist them to the database. This function runs every `self.poll_frequency`. """ self.log.info('syncing events') # Get a timestamp before polling, so that we don't subsequently miss # remote updates that happen while the poll loop is executing. sync_timestamp = datetime.utcnow() with session_scope() as db_session: account = db_session.query(Account).get(self.account_id) last_sync = account.last_synced_events try: deleted_uids, calendar_changes = self.provider.sync_calendars() except AccessNotEnabledError: self.log.warning( 'Access to provider calendar API not enabled; bypassing sync') return with session_scope() as db_session: handle_calendar_deletes(self.namespace_id, deleted_uids, self.log, db_session) calendar_uids_and_ids = handle_calendar_updates(self.namespace_id, calendar_changes, self.log, db_session) db_session.commit() for (uid, id_) in calendar_uids_and_ids: deleted_uids, event_changes = self.provider.sync_events( uid, sync_from_time=last_sync) with session_scope() as db_session: handle_event_deletes(self.namespace_id, id_, deleted_uids, self.log, db_session) handle_event_updates(self.namespace_id, id_, event_changes, self.log, db_session) db_session.commit() with session_scope() as db_session: account = db_session.query(Account).get(self.account_id) account.last_synced_events = sync_timestamp db_session.commit()
def remote_create_event(account, event, db_session, extra_args): provider = GoogleEventsProvider(account.id, account.namespace.id) result = provider.create_remote_event(event, **extra_args) # The events crud API assigns a random uid to an event when creating it. # We need to update it to the value returned by the Google calendar API. event.uid = result['id'] db_session.commit()
def remote_update_event(account, event, db_session): provider = GoogleEventsProvider(account.id, account.namespace.id) dump = provider.dump_event(event) service = provider._get_google_service() service.events().update(calendarId=event.calendar.name, eventId=event.uid, body=dump).execute()
def test_handle_api_not_enabled(): response = requests.Response() response.status_code = 403 response._content = json.dumps( { "error": { "code": 403, "message": "Access Not Configured.", "errors": [ { "domain": "usageLimits", "message": "Access Not Configured", "reason": "accessNotConfigured", "extendedHelp": "https://console.developers.google.com", } ], } } ).encode() requests.get = mock.Mock(return_value=response) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value="token") with pytest.raises(AccessNotEnabledError): provider._get_resource_list("https://googleapis.com/testurl")
def test_handle_quota_exceeded(): first_response = requests.Response() first_response.status_code = 403 first_response._content = json.dumps( { "error": { "errors": [ { "domain": "usageLimits", "reason": "userRateLimitExceeded", "message": "User Rate Limit Exceeded", } ], "code": 403, "message": "User Rate Limit Exceeded", } } ).encode() second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({"items": ["A", "B", "C"]}).encode() requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value="token") items = provider._get_resource_list("https://googleapis.com/testurl") # Check that we slept, then retried. assert gevent.sleep.called assert items == ["A", "B", "C"]
def test_handle_quota_exceeded(): first_response = requests.Response() first_response.status_code = 403 first_response._content = json.dumps({ 'error': { 'errors': [{ 'domain': 'usageLimits', 'reason': 'userRateLimitExceeded', 'message': 'User Rate Limit Exceeded' }], 'code': 403, 'message': 'User Rate Limit Exceeded' } }) second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({'items': ['A', 'B', 'C']}) requests.get = mock.Mock(side_effect=[first_response, second_response]) gevent.sleep = mock.Mock() provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') # Check that we slept, then retried. gevent.sleep.assert_called_once_with(30) assert items == ['A', 'B', 'C']
def test_handle_quota_exceeded(): first_response = requests.Response() first_response.status_code = 403 first_response._content = json.dumps({ 'error': { 'errors': [ {'domain': 'usageLimits', 'reason': 'userRateLimitExceeded', 'message': 'User Rate Limit Exceeded'} ], 'code': 403, 'message': 'User Rate Limit Exceeded' } }) second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({ 'items': ['A', 'B', 'C'] }) requests.get = mock.Mock(side_effect=[first_response, second_response]) gevent.sleep = mock.Mock() provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') # Check that we slept, then retried. assert gevent.sleep.called assert items == ['A', 'B', 'C']
class EventSync(BaseSyncMonitor): """Per-account event sync engine.""" def __init__(self, email_address, provider_name, account_id, namespace_id, poll_frequency=POLL_FREQUENCY): bind_context(self, 'eventsync', account_id) # Only Google for now, can easily parametrize by provider later. self.provider = GoogleEventsProvider(account_id, namespace_id) self.log = logger.new(account_id=account_id, component='calendar sync') BaseSyncMonitor.__init__(self, account_id, namespace_id, email_address, EVENT_SYNC_FOLDER_ID, EVENT_SYNC_FOLDER_NAME, provider_name, poll_frequency=poll_frequency, scope='calendar') def sync(self): """Query a remote provider for updates and persist them to the database. This function runs every `self.poll_frequency`. """ self.log.debug('syncing events') try: deleted_uids, calendar_changes = self.provider.sync_calendars() except AccessNotEnabledError: self.log.warning( 'Access to provider calendar API not enabled; bypassing sync') return with session_scope(self.namespace_id) as db_session: handle_calendar_deletes(self.namespace_id, deleted_uids, self.log, db_session) calendar_uids_and_ids = handle_calendar_updates( self.namespace_id, calendar_changes, self.log, db_session) db_session.commit() for (uid, id_) in calendar_uids_and_ids: # Get a timestamp before polling, so that we don't subsequently # miss remote updates that happen while the poll loop is executing. sync_timestamp = datetime.utcnow() with session_scope(self.namespace_id) as db_session: last_sync = db_session.query( Calendar.last_synced).filter(Calendar.id == id_).scalar() event_changes = self.provider.sync_events(uid, sync_from_time=last_sync) with session_scope(self.namespace_id) as db_session: handle_event_updates(self.namespace_id, id_, event_changes, self.log, db_session) cal = db_session.query(Calendar).get(id_) cal.last_synced = sync_timestamp db_session.commit()
def test_cancelled_override_creation(): # With showDeleted=True, we receive cancelled events (including instances # of recurring events) as full event objects, with status = 'cancelled'. # Test that we save this as a RecurringEventOverride rather than trying # to delete the UID. raw_response = [ { "created": "2012-10-09T22:35:50.000Z", "creator": { "displayName": "Eben Freeman", "email": "*****@*****.**", "self": True, }, "end": {"dateTime": "2012-10-22T19:00:00-07:00"}, "etag": '"2806773858144000"', "htmlLink": "https://www.google.com/calendar/event?eid=FOO", "iCalUID": "*****@*****.**", "id": "tn7krk4cekt8ag3pk6gapqqbro_20121022T170000Z", "kind": "calendar#event", "organizer": { "displayName": "Eben Freeman", "email": "*****@*****.**", "self": True, }, "attendees": [ { "displayName": "MITOC BOD", "email": "*****@*****.**", "responseStatus": "accepted", }, { "displayName": "Eben Freeman", "email": "*****@*****.**", "responseStatus": "accepted", }, ], "originalStartTime": { "dateTime": "2012-10-22T17:00:00-07:00", "timeZone": "America/Los_Angeles", }, "recurringEventId": "tn7krk4cekt8ag3pk6gapqqbro", "reminders": {"useDefault": True}, "sequence": 0, "start": { "dateTime": "2012-10-22T18:00:00-07:00", "timeZone": "America/Los_Angeles", }, "status": "cancelled", "summary": "BOD Meeting", } ] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events("uid", 1) assert updates[0].cancelled is True
def remote_create_event(account, event, db_session): provider = GoogleEventsProvider(account.id, account.namespace.id) dump = provider.dump_event(event) service = provider._get_google_service() result = service.events().insert(calendarId=event.calendar.name, body=dump).execute() # The events crud API assigns a random uid to an event when creating it. # We need to update it to the value returned by the Google calendar API. event.uid = result['id'] db_session.commit()
def test_handle_unparseable_dates(): raw_response = [{ 'id': '20140615_60o30dr564o30c1g60o30dr4ck', 'start': {'date': '0000-01-01'}, 'end': {'date': '0000-01-02'}, 'summary': 'test' }] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock( return_value=raw_response) updates = provider.sync_events('uid', 1) assert len(updates) == 0
def test_handle_unparseable_dates(): raw_response = [ { "id": "20140615_60o30dr564o30c1g60o30dr4ck", "start": {"date": "0000-01-01"}, "end": {"date": "0000-01-02"}, "summary": "test", } ] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events("uid", 1) assert len(updates) == 0
def test_cancelled_override_creation(): # With showDeleted=True, we receive cancelled events (including instances # of recurring events) as full event objects, with status = 'cancelled'. # Test that we save this as a RecurringEventOverride rather than trying # to delete the UID. raw_response = [{ 'created': '2012-10-09T22:35:50.000Z', 'creator': { 'displayName': 'Eben Freeman', 'email': '*****@*****.**', 'self': True }, 'end': {'dateTime': '2012-10-22T19:00:00-07:00'}, 'etag': '"2806773858144000"', 'htmlLink': 'https://www.google.com/calendar/event?eid=FOO', 'iCalUID': '*****@*****.**', 'id': 'tn7krk4cekt8ag3pk6gapqqbro_20121022T170000Z', 'kind': 'calendar#event', 'organizer': { 'displayName': 'Eben Freeman', 'email': '*****@*****.**', 'self': True }, 'attendees': [ {'displayName': 'MITOC BOD', 'email': '*****@*****.**', 'responseStatus': 'accepted'}, {'displayName': 'Eben Freeman', 'email': '*****@*****.**', 'responseStatus': 'accepted'} ], 'originalStartTime': { 'dateTime': '2012-10-22T17:00:00-07:00', 'timeZone': 'America/Los_Angeles' }, 'recurringEventId': 'tn7krk4cekt8ag3pk6gapqqbro', 'reminders': {'useDefault': True}, 'sequence': 0, 'start': {'dateTime': '2012-10-22T18:00:00-07:00', 'timeZone': 'America/Los_Angeles'}, 'status': 'cancelled', 'summary': 'BOD Meeting', }] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock( return_value=raw_response) updates = provider.sync_events('uid', 1) assert updates[0].cancelled is True
def test_pagination(): first_response = requests.Response() first_response.status_code = 200 first_response._content = json.dumps( {"items": ["A", "B", "C"], "nextPageToken": "CjkKKzlhb2tkZjNpZTMwNjhtZThllU"} ).encode() second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({"items": ["D", "E"]}).encode() requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value="token") items = provider._get_resource_list("https://googleapis.com/testurl") assert items == ["A", "B", "C", "D", "E"]
def test_handle_internal_server_error(): first_response = requests.Response() first_response.status_code = 503 second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({"items": ["A", "B", "C"]}).encode() requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value="token") items = provider._get_resource_list("https://googleapis.com/testurl") # Check that we slept, then retried. assert gevent.sleep.called assert items == ["A", "B", "C"]
def test_handle_http_401(): first_response = requests.Response() first_response.status_code = 401 second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({'items': ['A', 'B', 'C']}) requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') assert items == ['A', 'B', 'C'] # Check that we actually refreshed the access token assert len(provider._get_access_token.mock_calls) == 2
def __init__(self, email_address, provider_name, account_id, namespace_id, poll_frequency=POLL_FREQUENCY): bind_context(self, 'eventsync', account_id) # Only Google for now, can easily parametrize by provider later. self.provider = GoogleEventsProvider(account_id, namespace_id) self.log = logger.new(account_id=account_id, component='calendar sync') BaseSyncMonitor.__init__(self, account_id, namespace_id, email_address, EVENT_SYNC_FOLDER_ID, EVENT_SYNC_FOLDER_NAME, provider_name, poll_frequency=poll_frequency, scope='calendar')
def test_handle_internal_server_error(): first_response = requests.Response() first_response.status_code = 503 second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({'items': ['A', 'B', 'C']}) requests.get = mock.Mock(side_effect=[first_response, second_response]) gevent.sleep = mock.Mock() provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') # Check that we slept, then retried. gevent.sleep.assert_called_once_with(30) assert items == ['A', 'B', 'C']
def test_handle_http_401(): first_response = requests.Response() first_response.status_code = 401 second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({ 'items': ['A', 'B', 'C'] }) requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') assert items == ['A', 'B', 'C'] # Check that we actually refreshed the access token assert len(provider._get_access_token.mock_calls) == 2
def test_handle_internal_server_error(patched_gevent_sleep): first_response = requests.Response() first_response.status_code = 503 second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({ 'items': ['A', 'B', 'C'] }) requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') # Check that we slept, then retried. assert gevent.sleep.called assert items == ['A', 'B', 'C']
def test_handle_other_errors(): response = requests.Response() response.status_code = 403 response._content = "This is not the JSON you're looking for" requests.get = mock.Mock(return_value=response) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') with pytest.raises(requests.exceptions.HTTPError): provider._get_resource_list('https://googleapis.com/testurl') response = requests.Response() response.status_code = 404 requests.get = mock.Mock(return_value=response) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') with pytest.raises(requests.exceptions.HTTPError): provider._get_resource_list('https://googleapis.com/testurl')
def test_pagination(): first_response = requests.Response() first_response.status_code = 200 first_response._content = json.dumps({ 'items': ['A', 'B', 'C'], 'nextPageToken': 'CjkKKzlhb2tkZjNpZTMwNjhtZThllU' }) second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({'items': ['D', 'E']}) requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') assert items == ['A', 'B', 'C', 'D', 'E']
def get_api_access(db_session, email_address): account = db_session.query(Account).filter( Account.email_address == email_address).one() if account is None: raise Exception(("No account found for email address %s. " "Are you sure you've authed it?") % email_address) return GoogleEventsProvider(account.id, account.namespace.id).\ _get_google_service()
def test_pagination(): first_response = requests.Response() first_response.status_code = 200 first_response._content = json.dumps({ 'items': ['A', 'B', 'C'], 'nextPageToken': 'CjkKKzlhb2tkZjNpZTMwNjhtZThllU' }) second_response = requests.Response() second_response.status_code = 200 second_response._content = json.dumps({ 'items': ['D', 'E'] }) requests.get = mock.Mock(side_effect=[first_response, second_response]) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') items = provider._get_resource_list('https://googleapis.com/testurl') assert items == ['A', 'B', 'C', 'D', 'E']
def __init__(self, email_address, provider_name, account_id, namespace_id, poll_frequency=300): bind_context(self, 'eventsync', account_id) # Only Google for now, can easily parametrize by provider later. self.provider = GoogleEventsProvider(account_id, namespace_id) BaseSyncMonitor.__init__(self, account_id, namespace_id, email_address, EVENT_SYNC_FOLDER_ID, EVENT_SYNC_FOLDER_NAME, provider_name, poll_frequency=poll_frequency)
def test_handle_api_not_enabled(): response = requests.Response() response.status_code = 403 response._content = json.dumps({ 'error': { 'code': 403, 'message': 'Access Not Configured.', 'errors': [ {'domain': 'usageLimits', 'message': 'Access Not Configured', 'reason': 'accessNotConfigured', 'extendedHelp': 'https://console.developers.google.com'} ] } }) requests.get = mock.Mock(return_value=response) provider = GoogleEventsProvider(1, 1) provider._get_access_token = mock.Mock(return_value='token') with pytest.raises(AccessNotEnabledError): provider._get_resource_list('https://googleapis.com/testurl')
def __init__(self, email_address, provider_name, account_id, namespace_id, poll_frequency=POLL_FREQUENCY): bind_context(self, 'eventsync', account_id) # Only Google for now, can easily parametrize by provider later. self.provider = GoogleEventsProvider(account_id, namespace_id) BaseSyncMonitor.__init__(self, account_id, namespace_id, email_address, EVENT_SYNC_FOLDER_ID, EVENT_SYNC_FOLDER_NAME, provider_name, poll_frequency=poll_frequency)
def remote_delete_event(account, event, db_session): provider = GoogleEventsProvider(account.id, account.namespace.id) service = provider._get_google_service() service.events().delete(calendarId=event.calendar.name, eventId=event.uid).execute()
def remote_create(account, event, db_session): provider = GoogleEventsProvider(account.id, account.namespace.id) dump = provider.dump_event(event) service = provider._get_google_service() service.events().insert(calendarId='primary', body=dump).execute()
def test_calendar_parsing(): raw_response = [ { "accessRole": "owner", "backgroundColor": "#9a9cff", "colorId": "17", "defaultReminders": [{"method": "popup", "minutes": 30}], "etag": '"1425508164135000"', "foregroundColor": "#000000", "id": "*****@*****.**", "kind": "calendar#calendarListEntry", "notificationSettings": { "notifications": [ {"method": "email", "type": "eventCreation"}, {"method": "email", "type": "eventChange"}, {"method": "email", "type": "eventCancellation"}, {"method": "email", "type": "eventResponse"}, ] }, "primary": True, "selected": True, "summary": "*****@*****.**", "timeZone": "America/Los_Angeles", }, { "accessRole": "reader", "backgroundColor": "#f83a22", "colorId": "3", "defaultReminders": [], "description": "Holidays and Observances in United States", "etag": '"1399416119263000"', "foregroundColor": "#000000", "id": "en.usa#[email protected]", "kind": "calendar#calendarListEntry", "selected": True, "summary": "Holidays in United States", "timeZone": "America/Los_Angeles", }, { "defaultReminders": [], "deleted": True, "etag": '"1425952878772000"', "id": "*****@*****.**", "kind": "calendar#calendarListEntry", }, ] expected_deletes = ["*****@*****.**"] expected_updates = [ Calendar( uid="*****@*****.**", name="*****@*****.**", description=None, read_only=False, ), Calendar( uid="en.usa#[email protected]", name="Holidays in United States", description="Holidays and Observances in United States", read_only=True, ), ] provider = GoogleEventsProvider(1, 1) provider._get_raw_calendars = mock.MagicMock(return_value=raw_response) deletes, updates = provider.sync_calendars() assert deletes == expected_deletes for obtained, expected in zip(updates, expected_updates): assert cmp_cal_attrs(obtained, expected)
def test_event_parsing(): raw_response = [ { "created": "2012-10-09T22:35:50.000Z", "creator": { "displayName": "Eben Freeman", "email": "*****@*****.**", "self": True, }, "end": {"dateTime": "2012-10-15T18:00:00-07:00"}, "etag": '"2806773858144000"', "htmlLink": "https://www.google.com/calendar/event?eid=FOO", "iCalUID": "*****@*****.**", "id": "tn7krk4cekt8ag3pk6gapqqbro", "kind": "calendar#event", "organizer": { "displayName": "Eben Freeman", "email": "*****@*****.**", "self": True, }, "attendees": [ { "displayName": "MITOC BOD", "email": "*****@*****.**", "responseStatus": "accepted", }, { "displayName": "Eben Freeman", "email": "*****@*****.**", "responseStatus": "accepted", }, ], "reminders": {"useDefault": True}, "recurrence": ["RRULE:FREQ=WEEKLY;UNTIL=20150209T075959Z;BYDAY=MO"], "sequence": 0, "start": {"dateTime": "2012-10-15T17:00:00-07:00"}, "status": "confirmed", "summary": "BOD Meeting", "updated": "2014-06-21T21:42:09.072Z", }, { "created": "2014-01-09T03:33:02.000Z", "creator": { "displayName": "Holidays in United States", "email": "en.usa#[email protected]", "self": True, }, "end": {u"date": "2014-06-16"}, "etag": '"2778476764000000"', "htmlLink": "https://www.google.com/calendar/event?eid=BAR", "iCalUID": "*****@*****.**", "id": "20140615_60o30dr564o30c1g60o30dr4ck", "kind": "calendar#event", "organizer": { "displayName": "Holidays in United States", "email": "en.usa#[email protected]", "self": True, }, "sequence": 0, "start": {"date": "2014-06-15"}, "status": "confirmed", "summary": "Fathers' Day", "transparency": "transparent", "updated": "2014-01-09T03:33:02.000Z", "visibility": "public", }, { "created": "2015-03-10T01:19:59.000Z", "creator": { "displayName": "Ben Bitdiddle", "email": "*****@*****.**", "self": True, }, "end": {u"date": "2015-03-11"}, "etag": '"2851906839480000"', "htmlLink": "https://www.google.com/calendar/event?eid=BAZ", "iCalUID": "*****@*****.**", "id": "3uisajkmdjqo43tfc3ig1l5hek", "kind": "calendar#event", "organizer": { "displayName": "Ben Bitdiddle", "email": "*****@*****.**", "self": True, }, "reminders": {u"useDefault": False}, "sequence": 1, "start": {u"date": "2015-03-10"}, "status": "cancelled", "summary": "TUESDAY", "transparency": "transparent", "updated": "2015-03-10T02:10:19.740Z", }, ] expected_deletes = ["3uisajkmdjqo43tfc3ig1l5hek"] expected_updates = [ Event.create( uid="tn7krk4cekt8ag3pk6gapqqbro", title="BOD Meeting", description=None, read_only=False, start=arrow.get(2012, 10, 16, 0, 0, 0), end=arrow.get(2012, 10, 16, 1, 0, 0), all_day=False, busy=True, owner="Eben Freeman <*****@*****.**>", recurrence=["RRULE:FREQ=WEEKLY;UNTIL=20150209T075959Z;BYDAY=MO"], participants=[ { "email": "*****@*****.**", "name": "MITOC BOD", "status": "yes", "notes": None, }, { "email": "*****@*****.**", "name": "Eben Freeman", "status": "yes", "notes": None, }, ], ), Event.create( uid="20140615_60o30dr564o30c1g60o30dr4ck", title="Fathers' Day", description=None, read_only=False, busy=False, start=arrow.get(2014, 6, 15), end=arrow.get(2014, 6, 15), all_day=True, owner="Holidays in United States <en.usa#[email protected]>", participants=[], ), ] provider = GoogleEventsProvider(1, 1) provider.calendars_table = {"uid": False} provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events("uid", 1) # deleted events are actually only marked as # cancelled. Look for them in the updates stream. found_cancelled_event = False for event in updates: if event.uid in expected_deletes and event.status == "cancelled": found_cancelled_event = True break assert found_cancelled_event for obtained, expected in zip(updates, expected_updates): print(obtained, expected) assert cmp_event_attrs(obtained, expected) # Test read-only support raw_response = [ { "created": "2014-01-09T03:33:02.000Z", "creator": { "displayName": "Holidays in United States", "email": "en.usa#[email protected]", "self": True, }, "end": {u"date": "2014-06-16"}, "etag": '"2778476764000000"', "htmlLink": "https://www.google.com/calendar/event?eid=BAR", "iCalUID": "*****@*****.**", "id": "20140615_60o30dr564o30c1g60o30dr4ck", "kind": "calendar#event", "organizer": { "displayName": "Holidays in United States", "email": "en.usa#[email protected]", "self": True, }, "sequence": 0, "start": {"date": "2014-06-15"}, "status": "confirmed", "summary": "Fathers' Day", "transparency": "transparent", "updated": "2014-01-09T03:33:02.000Z", "visibility": "public", "guestCanModify": True, } ] provider = GoogleEventsProvider(1, 1) # This is a read-only calendar provider.calendars_table = {"uid": True} provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events("uid", 1) assert len(updates) == 1 assert updates[0].read_only is True
def test_calendar_parsing(): raw_response = [{ 'accessRole': 'owner', 'backgroundColor': '#9a9cff', 'colorId': '17', 'defaultReminders': [{ 'method': 'popup', 'minutes': 30 }], 'etag': '"1425508164135000"', 'foregroundColor': '#000000', 'id': '*****@*****.**', 'kind': 'calendar#calendarListEntry', 'notificationSettings': { 'notifications': [{ 'method': 'email', 'type': 'eventCreation' }, { 'method': 'email', 'type': 'eventChange' }, { 'method': 'email', 'type': 'eventCancellation' }, { 'method': 'email', 'type': 'eventResponse' }] }, 'primary': True, 'selected': True, 'summary': '*****@*****.**', 'timeZone': 'America/Los_Angeles' }, { 'accessRole': 'reader', 'backgroundColor': '#f83a22', 'colorId': '3', 'defaultReminders': [], 'description': 'Holidays and Observances in United States', 'etag': '"1399416119263000"', 'foregroundColor': '#000000', 'id': 'en.usa#[email protected]', 'kind': 'calendar#calendarListEntry', 'selected': True, 'summary': 'Holidays in United States', 'timeZone': 'America/Los_Angeles' }, { 'defaultReminders': [], 'deleted': True, 'etag': '"1425952878772000"', 'id': '*****@*****.**', 'kind': 'calendar#calendarListEntry' }] expected_deletes = ['*****@*****.**'] expected_updates = [ Calendar(uid='*****@*****.**', name='*****@*****.**', description=None, read_only=False), Calendar(uid='en.usa#[email protected]', name='Holidays in United States', description='Holidays and Observances in United States', read_only=True) ] provider = GoogleEventsProvider(1, 1) provider._get_raw_calendars = mock.MagicMock(return_value=raw_response) deletes, updates = provider.sync_calendars() assert deletes == expected_deletes for obtained, expected in zip(updates, expected_updates): assert cmp_cal_attrs(obtained, expected)
def test_calendar_parsing(): raw_response = [ { 'accessRole': 'owner', 'backgroundColor': '#9a9cff', 'colorId': '17', 'defaultReminders': [{'method': 'popup', 'minutes': 30}], 'etag': '"1425508164135000"', 'foregroundColor': '#000000', 'id': '*****@*****.**', 'kind': 'calendar#calendarListEntry', 'notificationSettings': { 'notifications': [ {'method': 'email', 'type': 'eventCreation'}, {'method': 'email', 'type': 'eventChange'}, {'method': 'email', 'type': 'eventCancellation'}, {'method': 'email', 'type': 'eventResponse'} ] }, 'primary': True, 'selected': True, 'summary': '*****@*****.**', 'timeZone': 'America/Los_Angeles' }, { 'accessRole': 'reader', 'backgroundColor': '#f83a22', 'colorId': '3', 'defaultReminders': [], 'description': 'Holidays and Observances in United States', 'etag': '"1399416119263000"', 'foregroundColor': '#000000', 'id': 'en.usa#[email protected]', 'kind': 'calendar#calendarListEntry', 'selected': True, 'summary': 'Holidays in United States', 'timeZone': 'America/Los_Angeles' }, { 'defaultReminders': [], 'deleted': True, 'etag': '"1425952878772000"', 'id': '*****@*****.**', 'kind': 'calendar#calendarListEntry' } ] expected_deletes = ['*****@*****.**'] expected_updates = [ Calendar(uid='*****@*****.**', name='*****@*****.**', description=None, read_only=False), Calendar(uid='en.usa#[email protected]', name='Holidays in United States', description='Holidays and Observances in United States', read_only=True) ] provider = GoogleEventsProvider(1, 1) provider._get_raw_calendars = mock.MagicMock( return_value=raw_response) deletes, updates = provider.sync_calendars() assert deletes == expected_deletes for obtained, expected in zip(updates, expected_updates): assert cmp_cal_attrs(obtained, expected)
'status': 'yes', 'notes': None} ]), Event(uid='20140615_60o30dr564o30c1g60o30dr4ck', title="Fathers' Day", description=None, read_only=False, busy=False, start=arrow.get(2014, 06, 15), end=arrow.get(2014, 06, 15), all_day=True, owner='Holidays in United States <en.usa#[email protected]>', participants=[]) ] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock( return_value=raw_response) updates = provider.sync_events('uid', 1) # deleted events are actually only marked as # cancelled. Look for them in the updates stream. found_cancelled_event = False for event in updates: if event.uid in expected_deletes and event.status == 'cancelled': found_cancelled_event = True break assert found_cancelled_event for obtained, expected in zip(updates, expected_updates):
}]), Event( uid='20140615_60o30dr564o30c1g60o30dr4ck', title="Fathers' Day", description=None, read_only=False, busy=False, start=arrow.get(2014, 06, 15), end=arrow.get(2014, 06, 15), all_day=True, owner= 'Holidays in United States <en.usa#[email protected]>', participants=[]) ] provider = GoogleEventsProvider(1, 1) provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events('uid', 1) # deleted events are actually only marked as # cancelled. Look for them in the updates stream. found_cancelled_event = False for event in updates: if event.uid in expected_deletes and event.status == 'cancelled': found_cancelled_event = True break assert found_cancelled_event for obtained, expected in zip(updates, expected_updates): print obtained, expected
}]), Event( uid='20140615_60o30dr564o30c1g60o30dr4ck', title="Fathers' Day", description=None, read_only=False, busy=False, start=arrow.get(2014, 06, 15), end=arrow.get(2014, 06, 15), all_day=True, owner= 'Holidays in United States <en.usa#[email protected]>', participants=[]) ] provider = GoogleEventsProvider(1, 1) provider.calendars_table = {'uid': False} provider._get_raw_events = mock.MagicMock(return_value=raw_response) updates = provider.sync_events('uid', 1) # deleted events are actually only marked as # cancelled. Look for them in the updates stream. found_cancelled_event = False for event in updates: if event.uid in expected_deletes and event.status == 'cancelled': found_cancelled_event = True break assert found_cancelled_event for obtained, expected in zip(updates, expected_updates):
def google_events_provider(config, db): return GoogleEventsProvider(ACCOUNT_ID, NAMESPACE_ID)
'status': 'yes', 'notes': None} ]), Event(uid='20140615_60o30dr564o30c1g60o30dr4ck', title="Fathers' Day", description=None, read_only=False, busy=False, start=arrow.get(2014, 06, 15), end=arrow.get(2014, 06, 15), all_day=True, owner='Holidays in United States <en.usa#[email protected]>', participants=[]) ] provider = GoogleEventsProvider(1, 1) provider.calendars_table = {'uid': False} provider._get_raw_events = mock.MagicMock( return_value=raw_response) updates = provider.sync_events('uid', 1) # deleted events are actually only marked as # cancelled. Look for them in the updates stream. found_cancelled_event = False for event in updates: if event.uid in expected_deletes and event.status == 'cancelled': found_cancelled_event = True break assert found_cancelled_event
def remote_delete_event(account, event_uid, calendar_name, calendar_uid, db_session, extra_args): provider = GoogleEventsProvider(account.id, account.namespace.id) provider.delete_remote_event(calendar_uid, event_uid, **extra_args)
def remote_update_event(account, event, db_session, extra_args): provider = GoogleEventsProvider(account.id, account.namespace.id) provider.update_remote_event(event, **extra_args)