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__))
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)
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)
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')
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)
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)
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)
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
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()
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()
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()