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 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_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_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_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)
Exemple #6
0
    def send_message(
        self,
        message: messaging.Message,
        skip_registration_id_lookup: bool = False,
        additional_registration_ids: Sequence[str] = None,
        app: "firebase_admin.App" = SETTINGS["DEFAULT_FIREBASE_APP"],
        **more_send_message_kwargs,
    ) -> FirebaseResponseDict:
        """
        Send notification of single message for all active devices in
        queryset and deactivate if DELETE_INACTIVE_DEVICES setting is set to True.
        Bulk sends using firebase.messaging.send_all. For every 500 messages, we send a
        single HTTP request to Firebase (the 500 is set by the firebase-sdk).

        :param message: firebase.messaging.Message. If `message` includes a token/id, it
        will be overridden.
        :param skip_registration_id_lookup: skips the QuerySet lookup and solely uses
        the list of IDs from additional_registration_ids
        :param additional_registration_ids: specific registration_ids to add to the
        :param app: firebase_admin.App. Specify a specific app to use
        QuerySet lookup
        :param more_send_message_kwargs: Parameters for firebase.messaging.send_all()
        - dry_run: bool. Whether to actually send the notification to the device
        If there are any new parameters, you can still specify them here.

        :raises FirebaseError
        :returns FirebaseResponseDict
        """
        registration_ids = self.get_registration_ids(
            skip_registration_id_lookup,
            additional_registration_ids,
        )
        if not registration_ids:
            return self.get_default_send_message_response()
        responses: List[messaging.SendResponse] = []
        for i in range(0, len(registration_ids), MAX_MESSAGES_PER_BATCH):
            messages = [
                self._prepare_message(m, t) for m, t in zip(
                    repeat(message, MAX_MESSAGES_PER_BATCH),
                    registration_ids[i:i + MAX_MESSAGES_PER_BATCH],
                )
            ]
            responses.extend(
                messaging.send_all(messages,
                                   app=app,
                                   **more_send_message_kwargs).responses)
        return FirebaseResponseDict(
            response=messaging.BatchResponse(responses),
            registration_ids_sent=registration_ids,
            deactivated_registration_ids=self.
            deactivate_devices_with_error_results(registration_ids, responses),
        )
    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_failed(self):
     batch_response = messaging.BatchResponse(
         [messaging.SendResponse(None, 'a')])
     request = FCMRequest(app=self.app,
                          notification=MockNotification(),
                          tokens=['abc', 'def'])
     with patch.object(
             messaging, 'send_multicast',
             return_value=batch_response) as mock_send, patch.object(
                 request, 'defer_track_notification') as mock_track:
         response = request.send()
     mock_send.assert_called_once()
     mock_track.assert_not_called()
     self.assertEqual(response, batch_response)
    def test_ping_fcm_fail(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')

        batch_response = messaging.BatchResponse(
            [messaging.SendResponse(None, FirebaseError(500, 'testing'))])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response) as mock_send:
            success = TBANSHelper._ping_client(client)
            mock_send.assert_called_once()
            self.assertFalse(success)
    def test_send_fcm_batch(self):
        clients = [
            MobileClient(parent=ndb.Key(Account, 'user_id'),
                         user_id='user_id',
                         messaging_id='{}'.format(i),
                         client_type=ClientType.OS_IOS) for i in range(3)
        ]

        batch_response = messaging.BatchResponse([])
        with patch('models.notifications.requests.fcm_request.MAXIMUM_TOKENS',
                   2), patch.object(FCMRequest,
                                    'send',
                                    return_value=batch_response) as mock_send:
            exit_code = TBANSHelper._send_fcm(clients, MockNotification())
            self.assertEqual(mock_send.call_count, 2)
            self.assertEqual(exit_code, 0)
Exemple #11
0
 def get_default_send_message_response() -> FirebaseResponseDict:
     return FirebaseResponseDict(
         response=messaging.BatchResponse([]),
         registration_ids_sent=[],
         deactivated_registration_ids=[],
     )
 def _patched_send_multicast(*args, **kwargs):
     return messaging.BatchResponse(responses)