Beispiel #1
0
    def fire(self, integration, obj):
        """
        A wrapper around `send_robust` with logging and type conversion
        """
        if self.has_listeners():
            # type conversion: we don't want plain dict as an obj
            if not isinstance(obj, self.model):
                obj = self.model(obj, integration.api)

            # note: we use "select_related('user')" to make sure we don't
            # have a separate query for integration.user over here
            with ctx(integration=integration, user=integration.user):
                extra = {'signal_name': self.name, 'obj': obj}
                logger.debug('Send Todoist event', extra=extra)
                resp = self.send_robust(None, integration=integration, obj=obj)
                for func, item in resp:
                    # log exceptions
                    if isinstance(item, Exception):
                        exc_info = (item.__class__, item, item.__traceback__)
                        logger.error('Todoist event handler %s() raises %r',
                                     func.__name__,
                                     item,
                                     exc_info=exc_info,
                                     extra=dict(extra,
                                                func_name=func.__name__))
Beispiel #2
0
def sync_evernote(integration_id):
    try:
        integration = Integration.objects.get(id=integration_id)
    except Integration.DoesNotExist:
        return
    with ctx(user=integration.user, integration=integration):
        utils.sync_evernote(integration)
Beispiel #3
0
def create_calendar(integration_id):
    try:
        integration = Integration.objects.get(id=integration_id)
    except Integration.DoesNotExist:
        return
    with ctx(user=integration.user, integration=integration):
        calendar = utils.get_or_create_todoist_calendar(integration)
        utils.subscribe_to_todoist_calendar(integration, calendar)
Beispiel #4
0
def sync_evernote(integration_id):
    try:
        integration = Integration.objects.select_related('user').get(id=integration_id)
    except Integration.DoesNotExist:
        return
    with ctx(user=integration.user, integration=integration):
        try:
            utils.sync_evernote(integration)
        except SoftTimeLimitExceeded:
            logger.error('Synchronization with Evernote took too long and was aborted')
Beispiel #5
0
 def lock_and_ctx(self):
     """
     Internal function returning a "global redis lock" to make sure we
     perform only one sync operation at a time.
     """
     lock_name = 'sync-bridge-%s-%s' % (self.integration.id, self.name)
     lock_timeout = 60 * 5  # no more than 5 mins per sync operation
     blocking_timeout = 30  # no more than 30 seconds waiting for the lock
     with ctx(integration=self.integration, user=self.integration.user):
         return get_redis().lock(lock_name, timeout=lock_timeout,
                                 blocking_timeout=blocking_timeout)
Beispiel #6
0
def initial_stateless_sync(integration_id):
    """
    The sync command which is performed for "stateless integrations"
    """
    try:
        integration = Integration.objects.get(pk=integration_id)
    except Integration.DoesNotExist:
        return
    api = sync.StatefulTodoistAPI.create(integration)
    with ctx(user=integration.user, integration=integration):
        api.sync(resource_types=['projects', 'items', 'notes'],
                 save_state=False)
Beispiel #7
0
    def run(self):
        # find the task function object
        task_fun = self.get_task_fun()
        if not task_fun:
            # unknown periodic task, destroy itself
            self.delete()
            return

        with ctx(user=self.integration.user, integration=self.integration):
            # 2. run the task as being asked
            logger.debug('Run periodic task %r', self.name)
            task_fun.func(self.integration)
Beispiel #8
0
    def run(self):
        # find the task function object
        task_fun = self.get_task_fun()
        if not task_fun:
            # unknown periodic task, destroy itself
            self.delete()
            return

        with ctx(user=self.integration.user, integration=self.integration):
            # 2. run the task as being asked
            logger.debug('Run periodic task %r', self.name)
            task_fun.func(self.integration)
Beispiel #9
0
 def lock_and_ctx(self):
     """
     Internal function returning a "global redis lock" to make sure we
     perform only one sync operation at a time.
     """
     lock_name = 'sync-bridge-%s-%s' % (self.integration.id, self.name)
     lock_timeout = 60 * 5  # no more than 5 mins per sync operation
     blocking_timeout = 30  # no more than 30 seconds waiting for the lock
     with ctx(integration=self.integration, user=self.integration.user):
         return get_redis().lock(lock_name,
                                 timeout=lock_timeout,
                                 blocking_timeout=blocking_timeout)
Beispiel #10
0
def stop_channel(user_id, channel_id, resource_id):
    """
    Stop channel if integration does not exist
    """
    try:
        user = User.objects.get(id=user_id)
    except User.DoesNotExist:
        return

    with ctx(user=user):
        client = get_authorized_client(user)
        try:
            json_post(client, '/channels/stop', id=channel_id, resouceId=resource_id)
        except HTTPError:
            # FIXME: it doesn't work :/
            pass
Beispiel #11
0
def accept_webhook(request, webhook_secret_key):
    """
    Webhooks recipient, as described here:

    https://dev.evernote.com/doc/articles/polling_notification.php
    """
    if settings.EVERNOTE_WEBHOOK_SECRET_KEY != webhook_secret_key:
        # not an Evernote request
        return HttpResponse()

    # "Webhook reasons" we react on: Evernote note created, Evernote note
    # updated. Everything else is quietly ignored
    reasons = {'create', 'update'}
    reason = request.GET.get('reason')
    if reason not in reasons:
        return HttpResponse()

    # We react on userId and notebookGuid. If we have an integration to
    # sync, schedule a new evernote synchronization, but make sure we don't
    # perform sync more often than once in a minute
    try:
        user_id = int(request.GET.get('userId'))
    except (TypeError, ValueError):
        return HttpResponse()
    notebook_guid = request.GET.get('notebookGuid')

    # most likely, it's going to be no more than one cache object, and no
    # more than one integration. Two or more Evernote accounts connected to
    # the same client, or more than one Evernote integration per account
    # is an exception
    for cache in (EvernoteAccountCache.objects
                          .filter(evernote_user_id=user_id)
                          .select_related('user')):
        for integration in cache.user.integration_set.filter(service_id='evernote_sync'):
            notebooks = integration.settings.get('evernote_notebooks') or []
            if notebook_guid in notebooks:
                with ctx(integration=integration, user=integration.user):
                    logger.debug('Received Evernote webhook. Schedule Sync')
                    subtask = tasks.sync_evernote.s(integration.id)
                    schedule_with_rate_limit(integration.id, 'last_sync', subtask, timeout=120)

    return HttpResponse()
Beispiel #12
0
def accept_webhook(request, integration_id):
    """
    Google Calendar webhook handler

    For more details see
    https://developers.google.com/google-apps/calendar/v3/push?hl=en_US#receiving-notifications
    """
    try:
        channel_id = request.META['HTTP_X_GOOG_CHANNEL_ID']
        resource_id = request.META['HTTP_X_GOOG_RESOURCE_ID']
        resource_state = request.META['HTTP_X_GOOG_RESOURCE_STATE']
        resource_uri = request.META['HTTP_X_GOOG_RESOURCE_URI']
        token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
    except KeyError:
        # not a google request
        return HttpResponse()

    try:
        token_data = utils.validate_webhook_token(token)
    except PowerAppError:
        return HttpResponse()

    try:
        integration = Integration.objects.get(id=integration_id)
    except Integration.DoesNotExist:
        tasks.stop_channel.delay(token_data['u'], channel_id, resource_id)
    else:
        with ctx(integration=integration, user=integration.user):
            logging_extra = {
                'channel_id': channel_id,
                'token': token,
                'resource_id': resource_id,
                'resource_state': resource_state,
                'resource_uri': resource_uri
            }
            logger.debug('Received GCal webhook. Schedule Sync',
                         extra=logging_extra)
            subtask = tasks.sync_gcal.s(integration_id)
            schedule_with_rate_limit(integration_id, 'last_sync', subtask)

    return HttpResponse()
Beispiel #13
0
def accept_webhook(request, integration_id):
    """
    Google Calendar webhook handler

    For more details see
    https://developers.google.com/google-apps/calendar/v3/push?hl=en_US#receiving-notifications
    """
    try:
        channel_id = request.META['HTTP_X_GOOG_CHANNEL_ID']
        resource_id = request.META['HTTP_X_GOOG_RESOURCE_ID']
        resource_state = request.META['HTTP_X_GOOG_RESOURCE_STATE']
        resource_uri = request.META['HTTP_X_GOOG_RESOURCE_URI']
        token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
    except KeyError:
        # not a google request
        return HttpResponse()

    try:
        token_data = utils.validate_webhook_token(token)
    except PowerAppError:
        return HttpResponse()

    try:
        integration = Integration.objects.get(id=integration_id)
    except Integration.DoesNotExist:
        tasks.stop_channel.delay(token_data['u'], channel_id, resource_id)
    else:
        with ctx(integration=integration, user=integration.user):
            logging_extra = {'channel_id': channel_id,
                             'token': token,
                             'resource_id': resource_id,
                             'resource_state': resource_state,
                             'resource_uri': resource_uri}
            logger.debug('Received GCal webhook. Schedule Sync', extra=logging_extra)
            subtask = tasks.sync_gcal.s(integration_id)
            schedule_with_rate_limit(integration_id, 'last_sync', subtask)

    return HttpResponse()
Beispiel #14
0
    def fire(self, integration, obj):
        """
        A wrapper around `send_robust` with logging and type conversion
        """
        if self.has_listeners():
            # type conversion: we don't want plain dict as an obj
            if not isinstance(obj, self.model):
                obj = self.model(obj, integration.api)

            # note: we use "select_related('user')" to make sure we don't
            # have a separate query for integration.user over here
            with ctx(integration=integration, user=integration.user):
                extra = {'signal_name': self.name, 'obj': obj}
                logger.debug('Send Todoist event', extra=extra)
                resp = self.send_robust(None, integration=integration, obj=obj)
                for func, item in resp:
                    # log exceptions
                    if isinstance(item, Exception):
                        exc_info = (item.__class__, item, item.__traceback__)
                        logger.error('Todoist event handler %s() raises %r',
                                     func.__name__, item,
                                     exc_info=exc_info,
                                     extra=dict(extra, func_name=func.__name__))