Example #1
0
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()
Example #2
0
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()
Example #3
0
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()
Example #4
0
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")
Example #5
0
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"]
Example #6
0
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']
Example #8
0
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()
Example #9
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
Example #10
0
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()
Example #11
0
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
Example #13
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
Example #14
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
Example #15
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
Example #16
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
Example #17
0
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"]
Example #18
0
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"]
Example #19
0
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
Example #20
0
    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')
Example #21
0
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']
Example #22
0
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']
Example #24
0
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')
Example #25
0
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']
Example #26
0
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()
Example #27
0
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']
Example #28
0
    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)
Example #29
0
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')
Example #30
0
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)
Example #32
0
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')
Example #33
0
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()
Example #34
0
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()
Example #35
0
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)
Example #36
0
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
Example #37
0
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)
Example #38
0
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):
Example #40
0
            }]),
        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
Example #41
0
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()
Example #42
0
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()
Example #43
0
            }]),
        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):
Example #44
0
def google_events_provider(config, db):
    return GoogleEventsProvider(ACCOUNT_ID, NAMESPACE_ID)
Example #45
0
                            '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
Example #46
0
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)
Example #47
0
def remote_update_event(account, event, db_session, extra_args):
    provider = GoogleEventsProvider(account.id, account.namespace.id)
    provider.update_remote_event(event, **extra_args)