Esempio n. 1
0
    def postUpdateHook(cls, awards, updated_attr_list, is_new_list):
        # Note, updated_attr_list will always be empty, for now
        # Still needs to be implemented in updateMerge
        # See helpers.EventManipulator
        events = []
        for (award, updated_attrs) in zip(awards, updated_attr_list):
            event = award.event
            if event not in events:
                events.append(event)

        for event in events:
            if event.get().within_a_day:
                try:
                    NotificationHelper.send_award_update(event.get())
                except Exception:
                    logging.error("Error sending award update for {}".format(
                        event.id()))
                try:
                    TBANSHelper.awards(event.get())
                except Exception:
                    logging.error("Error sending award update for {}".format(
                        event.id()))

        # Enqueue task to calculate district points
        for event in events:
            taskqueue.add(url='/tasks/math/do/district_points_calc/{}'.format(
                event.id()),
                          method='GET')
 def test_send_empty(self):
     notification = MockNotification()
     with patch.object(TBANSHelper, '_defer_fcm') as mock_fcm, patch.object(
             TBANSHelper, '_defer_webhook') as mock_webhook:
         TBANSHelper._send([], notification)
         mock_fcm.assert_not_called()
         mock_webhook.assert_not_called()
    def test_send_fcm_retry_backoff_time(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        import time

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None, QuotaExceededError('code', 'message'))
        ])
        for i in range(0, 6):
            with patch.object(
                    FCMRequest, 'send',
                    return_value=batch_response), patch('logging.error'):
                call_time = time.time()
                TBANSHelper._send_fcm([client], MockNotification(), i)

                # Check that we queue'd for a retry with the proper countdown time
                tasks = self.taskqueue_stub.get_filtered_tasks(
                    queue_names='push-notifications')
                if i > 0:
                    self.assertGreater(tasks[0].eta_posix - call_time, 0)

                # Make sure our taskqueue tasks execute what we expect
                with patch.object(TBANSHelper, '_send_fcm') as mock_send_fcm:
                    deferred.run(tasks[0].payload)
                    mock_send_fcm.assert_called_once_with([client], ANY, i + 1)

                self.taskqueue_stub.FlushQueue('push-notifications')
    def test_broadcast_fcm(self):
        for client_type in ClientType.FCM_CLIENTS:
            client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                                  user_id='user_id',
                                  messaging_id='token',
                                  client_type=client_type,
                                  device_uuid='uuid',
                                  display_name='Phone')
            client_key = client.put()

            from notifications.base_notification import BaseNotification
            with patch.object(BaseNotification, 'send') as mock_send:
                TBANSHelper.broadcast([client_type], 'Broadcast',
                                      'Test broadcast')
                # Make sure we didn't send to Android
                mock_send.assert_not_called()

            # Make sure we'll send to FCM clients
            tasks = self.taskqueue_stub.get_filtered_tasks(
                queue_names='push-notifications')
            self.assertEqual(len(tasks), 1)

            # Make sure our taskqueue tasks execute what we expect
            with patch.object(TBANSHelper, '_send_fcm') as mock_send_fcm:
                deferred.run(tasks[0].payload)
                mock_send_fcm.assert_called_once_with([client], ANY)
                # Make sure the notification is a BroadcastNotification
                notification = mock_send_fcm.call_args[0][1]
                self.assertTrue(isinstance(notification,
                                           BroadcastNotification))

            self.taskqueue_stub.FlushQueue('push-notifications')

            client_key.delete()
 def test_awards(self):
     from notifications.awards_updated import AwardsUpdatedNotification
     with patch.object(TBANSHelper, '_send') as mock_send:
         TBANSHelper.awards(self.event)
         # Make sure we sent to FCM/webhooks
         mock_send.assert_called_once()
         notification = mock_send.call_args[0][1]
         self.assertTrue(isinstance(notification, AwardsNotification))
         self.assertEqual(notification.event, self.event)
    def test_broadcast_none(self):
        from notifications.base_notification import BaseNotification
        with patch.object(BaseNotification, 'send') as mock_send:
            TBANSHelper.broadcast([], 'Broadcast', 'Test broadcast')
            # Make sure we didn't send to Android
            mock_send.assert_not_called()

        # Make sure we didn't send to FCM or webhooks
        tasks = self.taskqueue_stub.GetTasks('push-notifications')
        self.assertEqual(len(tasks), 0)
    def test_ping_fcm_unsupported(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='token',
                              client_type=-1,
                              device_uuid='uuid',
                              display_name='Phone')

        with self.assertRaises(Exception,
                               msg='Unsupported FCM client type: -1'):
            TBANSHelper._ping_client(client)
    def test_send_fcm_maximum_backoff(self):
        for i in range(0, 6):
            exit_code = TBANSHelper._send_fcm([],
                                              MockNotification(),
                                              backoff_iteration=i)
            self.assertEqual(exit_code, 0)

        # Backoff should start failing at 6
        exit_code = TBANSHelper._send_fcm([],
                                          MockNotification(),
                                          backoff_iteration=6)
        self.assertEqual(exit_code, 2)
    def postUpdateHook(cls, event_details_list, updated_attr_list,
                       is_new_list):
        """
        To run after models have been updated
        """
        for (event_details, updated_attrs) in zip(event_details_list,
                                                  updated_attr_list):
            event = Event.get_by_id(event_details.key.id())
            if event.within_a_day and "alliance_selections" in updated_attrs:
                try:
                    NotificationHelper.send_alliance_update(event)
                except Exception:
                    logging.error(
                        "Error sending alliance update notification for {}".
                        format(event.key_name))
                    logging.error(traceback.format_exc())
                try:
                    TBANSHelper.alliance_selection(event)
                except Exception:
                    logging.error(
                        "Error sending alliance update notification for {}".
                        format(event.key_name))
                    logging.error(traceback.format_exc())

            # Enqueue task to calculate district points
            try:
                taskqueue.add(
                    url='/tasks/math/do/district_points_calc/{}'.format(
                        event.key.id()),
                    method='GET')
            except Exception:
                logging.error(
                    "Error enqueuing district_points_calc for {}".format(
                        event.key.id()))
                logging.error(traceback.format_exc())

            # Enqueue task to calculate event team status
            try:
                taskqueue.add(url='/tasks/math/do/event_team_status/{}'.format(
                    event.key.id()),
                              method='GET')
            except Exception:
                logging.error(
                    "Error enqueuing event_team_status for {}".format(
                        event.key.id()))
                logging.error(traceback.format_exc())

            try:
                FirebasePusher.update_event_details(event_details)
            except Exception:
                logging.warning("Firebase update_event_details failed!")
Esempio n. 10
0
    def ping_client(self, request):
        current_user = endpoints.get_current_user()
        if current_user is None:
            return BaseResponse(code=401,
                                message="Unauthorized to ping client")

        user_id = PushHelper.user_email_to_id(current_user.email())
        gcm_id = request.mobile_id

        # Find a Client for the current user with the passed GCM ID
        clients = MobileClient.query(MobileClient.messaging_id == gcm_id,
                                     ancestor=ndb.Key(Account,
                                                      user_id)).fetch(1)
        if len(clients) == 0:
            # No Client for user with that push token - bailing
            return BaseResponse(code=404,
                                message="Invalid push token for user")
        else:
            client = clients[0]
            from helpers.tbans_helper import TBANSHelper
            success = TBANSHelper.ping(client)
            if success:
                return BaseResponse(code=200, message="Ping sent")
            else:
                return BaseResponse(code=500, message="Failed to ping client")
    def test_send_fcm_unhandled_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        batch_response = messaging.BatchResponse(
            [messaging.SendResponse(None, FirebaseError('code', 'message'))])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.error') as mock_error:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_error.assert_called_once_with(
                'Unhandled FCM error for messaging_id - code / message')

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        # Check that we didn't queue for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
    def post(self):
        self._require_registration()

        # Check to make sure that they aren't trying to edit another user
        current_user_account_id = self.user_bundle.account.key.id()
        target_account_id = self.request.get('account_id')
        if target_account_id == current_user_account_id:
            url = self.request.get('url')
            secret_key = self.request.get('secret')
            query = MobileClient.query(MobileClient.messaging_id == url,
                                       ancestor=ndb.Key(
                                           Account, current_user_account_id))
            if query.count() == 0:
                # Webhook doesn't exist, add it
                response = TBANSHelper.verify_webhook(url, secret_key)
                client = MobileClient(
                    parent=self.user_bundle.account.key,
                    user_id=current_user_account_id,
                    messaging_id=url,
                    display_name=self.request.get('name'),
                    secret=secret_key,
                    client_type=ClientType.WEBHOOK,
                    verified=False,
                    verification_code=response.verification_key)
                client.put()
            else:
                # Webhook already exists. Update the secret
                current = query.fetch()[0]
                current.secret = secret_key
                current.put()
            self.redirect('/account')
        else:
            self.redirect('/')
    def post(self):
        self._require_registration()

        current_user_account_id = self.user_bundle.account.key.id()
        target_account_id = self.request.get('account_id')
        if target_account_id == current_user_account_id:
            client_id = self.request.get('client_id')
            webhook = MobileClient.get_by_id(int(client_id),
                                             parent=ndb.Key(
                                                 Account,
                                                 current_user_account_id))
            if webhook.client_type == ClientType.WEBHOOK and current_user_account_id == webhook.user_id:
                response = TBANSHelper.verify_webhook(webhook.messaging_id,
                                                      webhook.secret)
                webhook.verification_code = response.verification_key
                webhook.verified = False
                webhook.put()
                self.redirect('/account')
                return
            else:
                logging.warning("Not webhook, or wrong owner")
        else:
            logging.warning("Users don't match. " + current_user_account_id +
                            "/" + target_account_id)
        self.redirect('/')
    def test_send_fcm_unavailable_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None, UnavailableError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.error') as mock_error:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_error.assert_called_once_with(
                'FCM unavailable - retrying client...')

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        # Check that we queue'd for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 1)

        # Make sure our taskqueue tasks execute what we expect
        with patch.object(TBANSHelper, '_send_fcm') as mock_send_fcm:
            deferred.run(tasks[0].payload)
            mock_send_fcm.assert_called_once_with([client], ANY, 1)
    def test_send_fcm_invalid_argument_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None,
                                   InvalidArgumentError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.critical') as mock_critical:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_critical.assert_called_once_with(
                'Invalid argument when sending to FCM - code')

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        # Make sure we haven't queued for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
    def test_send_fcm_sender_id_mismatch_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None,
                                   SenderIdMismatchError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send', return_value=batch_response), \
            patch.object(MobileClient, 'delete_for_messaging_id', wraps=MobileClient.delete_for_messaging_id) as mock_delete, \
                patch('logging.info') as mock_info:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            mock_delete.assert_called_once_with('messaging_id')
            self.assertEqual(exit_code, 0)
            mock_info.assert_called_with(
                'Deleting mismatched client with ID: messaging_id')

        # Sanity check
        self.assertEqual(fcm_messaging_ids('user_id'), [])

        # Make sure we haven't queued for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
 def test_verification(self):
     from models.notifications.requests.webhook_request import WebhookRequest
     with patch.object(WebhookRequest, 'send') as mock_send:
         verification_key = TBANSHelper.verify_webhook(
             'https://thebluealliance.com', 'secret')
         mock_send.assert_called_once()
         self.assertIsNotNone(verification_key)
 def send_ping(cls, client):
     if client.client_type == ClientType.OS_ANDROID:
         notification = PingNotification()
         notification.send({client.client_type: [client.messaging_id]})
     else:
         # Send iOS/web/webhooks ping via TBANS
         return TBANSHelper.ping(client)
 def send_ping(cls, client):
     if client.client_type == ClientType.OS_ANDROID:
         notification = PingNotification()
         notification.send({client.client_type: [client.messaging_id]})
     else:
         # Send iOS/web/webhooks ping via TBANS
         return TBANSHelper.ping(client)
    def test_defer_webhook(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.WEBHOOK)
        client.put()
        notification = MockNotification()
        TBANSHelper._defer_webhook([client], notification)

        # Make sure we'll send to FCM clients
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 1)

        # Make sure our taskqueue tasks execute what we expect
        with patch.object(TBANSHelper, '_send_webhook') as mock_send_webhook:
            deferred.run(tasks[0].payload)
            mock_send_webhook.assert_called_once_with([client], ANY)
    def test_debug_string_response(self):
        class MockResponse:
            def json(self):
                import json
                return json.dumps({'mock': 'mock'})

        exception = FirebaseError('code', 'message', None, MockResponse())
        self.assertEqual(TBANSHelper._debug_string(exception),
                         'code / message / {"mock": "mock"}')
    def test_send_webhook_disabled(self):
        from sitevars.notifications_enable import NotificationsEnable
        NotificationsEnable.enable_notifications(False)

        with patch.object(NotificationsEnable,
                          'notifications_enabled',
                          wraps=NotificationsEnable.notifications_enabled
                          ) as mock_check_enabled:
            exit_code = TBANSHelper._send_webhook([], MockNotification())
            mock_check_enabled.assert_called_once()
            self.assertEqual(exit_code, 1)
    def test_broadcast_android(self):
        client_type = ClientType.OS_ANDROID
        messaging_id = 'token'

        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id=messaging_id,
                              client_type=client_type,
                              device_uuid='uuid',
                              display_name='Phone')
        client.put()

        from notifications.broadcast import BroadcastNotification
        with patch.object(BroadcastNotification, 'send') as mock_send:
            TBANSHelper.broadcast([client_type], 'Broadcast', 'Test broadcast')
            mock_send.assert_called_once_with({client_type: [messaging_id]})

        # Make sure we didn't send to FCM or webhooks
        tasks = self.taskqueue_stub.GetTasks('push-notifications')
        self.assertEqual(len(tasks), 0)
Esempio n. 24
0
    def post(self):
        self._require_admin()

        user_id = self.user_bundle.account.key.id()

        notification_type = self.request.get('type')
        if notification_type == "awards":
            event_key = self.request.get('event_key')
            event = Event.get_by_id(event_key)
            if not event:
                self.template_values.update(
                    {'error': 'No event for key {}'.format(event_key)})
                return self.redirect('/admin/tbans')

            TBANSHelper.awards(event, user_id)
        elif notification_type == "ping":
            clients = MobileClient.clients([user_id])
            for client in clients:
                TBANSHelper.ping(client)

        return self.redirect('/admin/tbans')
Esempio n. 25
0
 def postUpdateHook(cls, matches, updated_attr_list, is_new_list):
     '''
     To run after the match has been updated.
     Send push notifications to subscribed users
     Only if the match is part of an active event
     '''
     unplayed_match_events = []
     for (match, updated_attrs, is_new) in zip(matches, updated_attr_list,
                                               is_new_list):
         event = match.event.get()
         # Only continue if the event is currently happening
         if event.now:
             if match.has_been_played:
                 if is_new or 'alliances_json' in updated_attrs:
                     # There is a score update for this match, push a notification
                     logging.info(
                         "Sending push notifications for {}".format(
                             match.key_name))
                     try:
                         NotificationHelper.send_match_score_update(match)
                     except Exception, exception:
                         logging.error(
                             "Error sending match updates: {}".format(
                                 exception))
                         logging.error(traceback.format_exc())
                     try:
                         TBANSHelper.match_score(match)
                     except Exception, exception:
                         logging.error(
                             "Error sending match {} updates: {}".format(
                                 match.key_name, exception))
                         logging.error(traceback.format_exc())
             else:
                 if is_new or (set([
                         'alliances_json', 'time', 'time_string'
                 ]).intersection(set(updated_attrs)) != set()):
                     # The match has not been played and we're changing a property that affects the event's schedule
                     # So send a schedule update notification for the parent event
                     if event not in unplayed_match_events:
                         unplayed_match_events.append(event)
    def test_ping_client(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='token',
                              client_type=ClientType.OS_IOS,
                              device_uuid='uuid',
                              display_name='Phone')

        with patch.object(TBANSHelper, '_ping_client',
                          return_value=True) as mock_ping_client:
            success = TBANSHelper.ping(client)
            mock_ping_client.assert_called_once_with(client)
            self.assertTrue(success)
    def test_ping_webhook(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='https://thebluealliance.com',
                              client_type=ClientType.WEBHOOK,
                              secret='secret',
                              display_name='Webhook')

        with patch.object(TBANSHelper, '_ping_webhook',
                          return_value=True) as mock_ping_webhook:
            success = TBANSHelper.ping(client)
            mock_ping_webhook.assert_called_once_with(client)
            self.assertTrue(success)
    def test_ping_webhook_failure(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='https://thebluealliance.com',
                              client_type=ClientType.WEBHOOK,
                              secret='secret',
                              display_name='Webhook')

        from models.notifications.requests.webhook_request import WebhookRequest
        with patch.object(WebhookRequest, 'send',
                          return_value=False) as mock_send:
            success = TBANSHelper._ping_webhook(client)
            mock_send.assert_called_once()
            self.assertFalse(success)
Esempio n. 29
0
    def postUpdateHook(cls, awards, updated_attr_list, is_new_list):
        # Note, updated_attr_list will always be empty, for now
        # Still needs to be implemented in updateMerge
        # See helpers.EventManipulator
        events = []
        for (award, updated_attrs) in zip(awards, updated_attr_list):
            event = award.event
            if event not in events:
                events.append(event)

        for event in events:
            if event.get().within_a_day:
                try:
                    NotificationHelper.send_award_update(event.get())
                except Exception:
                    logging.error("Error sending award update for {}".format(
                        event.id()))
                try:
                    TBANSHelper.awards(event.get())
                except Exception, exception:
                    logging.error("Error sending {} award updates: {}".format(
                        event.id(), exception))
                    logging.error(traceback.format_exc())
    def test_send_webhook_multiple(self):
        clients = [
            MobileClient(parent=ndb.Key(Account, 'user_id'),
                         user_id='user_id',
                         messaging_id='{}'.format(i),
                         client_type=ClientType.WEBHOOK) for i in range(3)
        ]

        batch_response = messaging.BatchResponse([])
        with patch.object(WebhookRequest, 'send',
                          return_value=batch_response) as mock_send:
            exit_code = TBANSHelper._send_webhook(clients, MockNotification())
            self.assertEqual(mock_send.call_count, 3)
            self.assertEqual(exit_code, 0)
    def test_send(self):
        expected = [
            'client_type_{}'.format(client_type)
            for client_type in ClientType.FCM_CLIENTS
        ]
        clients = [
            MobileClient(parent=ndb.Key(Account, 'user_id'),
                         user_id='user_id',
                         messaging_id='client_type_{}'.format(client_type),
                         client_type=client_type)
            for client_type in ClientType.names.keys()
        ]
        # Insert an unverified webhook, just to test
        unverified = MobileClient(parent=ndb.Key(Account, 'user_id'),
                                  user_id='user_id',
                                  messaging_id='client_type_2',
                                  client_type=ClientType.WEBHOOK,
                                  verified=False)
        unverified.put()
        for c in clients:
            c.put()

        expected_fcm = [
            c for c in clients if c.client_type in ClientType.FCM_CLIENTS
        ]
        expected_webhook = [
            c for c in clients if c.client_type == ClientType.WEBHOOK
        ]

        notification = MockNotification()
        with patch.object(TBANSHelper, '_defer_fcm') as mock_fcm, patch.object(
                TBANSHelper, '_defer_webhook') as mock_webhook:
            TBANSHelper._send(['user_id'], notification)
            mock_fcm.assert_called_once_with(expected_fcm, notification)
            mock_webhook.assert_called_once_with(expected_webhook,
                                                 notification)
    def get(self):
        webhooks = MobileClient.query(MobileClient.client_type == ClientType.WEBHOOK).fetch()
        failures = []

        for client in webhooks:
            response = TBANSHelper.ping_webhook(client)
            if not response.code == 200:
                failures.append(client.key)

        count = len(failures)
        if failures:
            ndb.delete_multi(failures)
        logging.info("Deleted {} broken webhooks".format(count))

        template_values = {'count': count}
        path = os.path.join(os.path.dirname(__file__), '../../templates/admin/webhooks_clear_do.html')
        self.response.out.write(template.render(path, template_values))
    def post(self):
        self._require_registration()
        self._require_request_user_is_bundle_user()

        # Name and URL must be non-None
        url = self.request.get('url', None)
        name = self.request.get('name', None)
        if not url or not name:
            return self.redirect('/webhooks/add?error=1')

        # Secret may be none - but we'll generate a secret for the user
        secret = self.request.get('secret', None)
        if not secret:
            import uuid
            secret = uuid.uuid4().hex

        current_user_account_id = self.user_bundle.account.key.id()
        query = MobileClient.query(MobileClient.messaging_id == url, ancestor=ndb.Key(Account, current_user_account_id))
        if query.count() == 0:
            # Webhook doesn't exist, add it
            from helpers.tbans_helper import TBANSHelper
            response = TBANSHelper.verify_webhook(url, secret)

            client = MobileClient(
                parent=self.user_bundle.account.key,
                user_id=current_user_account_id,
                messaging_id=url,
                display_name=name,
                secret=secret,
                client_type=ClientType.WEBHOOK,
                verified=False,
                verification_code=response.verification_key)
            client.put()
        else:
            # Webhook already exists. Update the secret
            current = query.fetch()[0]
            current.secret = secret
            current.put()

        self.redirect('/account')
    def post(self):
        self._require_registration()
        self._require_request_user_is_bundle_user()

        current_user_account_id = self.user_bundle.account.key.id()
        if not current_user_account_id:
            return self.redirect('/')

        client_id = self.request.get('client_id')
        if not client_id:
            return self.redirect('/')

        webhook = MobileClient.get_by_id(int(client_id), parent=ndb.Key(Account, current_user_account_id))
        if not webhook or webhook.client_type != ClientType.WEBHOOK or current_user_account_id != webhook.user_id:
            return self.redirect('/')

        from helpers.tbans_helper import TBANSHelper
        response = TBANSHelper.verify_webhook(webhook.messaging_id, webhook.secret)

        webhook.verification_code = response.verification_key
        webhook.verified = False
        webhook.put()

        return self.redirect('/account')
 def test_create_service(self):
     service = TBANSHelper._create_service()
     self.assertIsNotNone(service)
 def test_ping_client(self):
     TBANSHelper._create_service = self._create_mock_service
     client = MobileClient(client_type=ClientType.OS_IOS)
     TBANSHelper.ping_client(client)
 def test_ping_webhook(self):
     TBANSHelper._create_service = self._create_mock_service
     client = MobileClient(client_type=ClientType.WEBHOOK)
     TBANSHelper.ping_webhook(client)
 def test_ping_webhook(self):
     TBANSHelper._create_service = self._create_mock_service
     TBANSHelper.verify_webhook(url='abc', secret='def')