예제 #1
0
    def _ping_client(client):
        client_type = client.client_type
        if client_type in ClientType.FCM_CLIENTS:
            from models.notifications.ping import PingNotification
            notification = PingNotification()

            from models.notifications.requests.fcm_request import FCMRequest
            fcm_request = FCMRequest(firebase_app,
                                     notification,
                                     tokens=[client.messaging_id])
            logging.info('Ping - {}'.format(str(fcm_request)))

            batch_response = fcm_request.send()
            if batch_response.failure_count > 0:
                response = batch_response.responses[0]
                logging.info('Error Sending Ping - {}'.format(
                    response.exception))
                return False
            else:
                logging.info('Ping Sent')
        elif client_type == ClientType.OS_ANDROID:
            # Send old notifications to Android
            from notifications.ping import PingNotification
            notification = PingNotification()
            notification.send({client_type: [client.messaging_id]})
        else:
            raise Exception(
                'Unsupported FCM client type: {}'.format(client_type))

        return True
예제 #2
0
 def test_fcm_message_empty(self):
     request = FCMRequest(self.app,
                          notification=MockNotification(),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertIsNotNone(message.data)
     self.assertIsNone(message.notification)
     self.assertIsNone(message.android)
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertIsNone(message.webpush)
     self.assertEqual(message.tokens, ['abc'])
예제 #3
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)
예제 #4
0
 def test_fcm_message_data_payload_default(self):
     platform_config = PlatformConfig(priority=PlatformPriority.HIGH,
                                      collapse_key='collapse_key')
     request = FCMRequest(self.app,
                          notification=MockNotification(),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertEqual(message.data, {'notification_type': 'verification'})
     self.assertIsNone(message.notification)
     self.assertIsNone(message.android)
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertIsNone(message.webpush)
     self.assertEqual(message.tokens, ['abc'])
예제 #5
0
 def test_fcm_message_platform_config(self):
     platform_config = PlatformConfig(priority=PlatformPriority.HIGH,
                                      collapse_key='collapse_key')
     request = FCMRequest(
         self.app,
         notification=MockNotification(platform_config=platform_config),
         tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertIsNotNone(message.data)
     self.assertIsNone(message.notification)
     self.assertTrue(isinstance(message.android, messaging.AndroidConfig))
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertTrue(isinstance(message.webpush, messaging.WebpushConfig))
     self.assertEqual(message.tokens, ['abc'])
예제 #6
0
 def test_str(self):
     request = FCMRequest(self.app,
                          notification=MockNotification(),
                          tokens=['abc'])
     self.assertEqual(
         "FCMRequest(tokens=['abc'], notification=MockNotification())",
         str(request))
예제 #7
0
 def test_fcm_message_notification(self):
     platform_config = PlatformConfig(priority=PlatformPriority.HIGH,
                                      collapse_key='collapse_key')
     request = FCMRequest(self.app,
                          notification=MockNotification(
                              fcm_notification=messaging.Notification(
                                  title='Title', body='Some body message')),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertIsNotNone(message.data)
     self.assertTrue(
         isinstance(message.notification, messaging.Notification))
     self.assertIsNone(message.android)
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertIsNone(message.webpush)
     self.assertEqual(message.tokens, ['abc'])
예제 #8
0
 def test_fcm_message_apns_content_available(self):
     request = FCMRequest(self.app,
                          notification=MockNotification(),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertIsNotNone(message.data)
     self.assertIsNone(message.notification)
     self.assertIsNone(message.android)
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertTrue(isinstance(message.apns.payload,
                                messaging.APNSPayload))
     self.assertTrue(isinstance(message.apns.payload.aps, messaging.Aps))
     self.assertIsNone(message.apns.payload.aps.sound)
     self.assertTrue(message.apns.payload.aps.content_available)
     self.assertIsNone(message.webpush)
     self.assertEqual(message.tokens, ['abc'])
예제 #9
0
 def test_init_delivery_too_many_tokens(self):
     with self.assertRaises(ValueError) as ex:
         FCMRequest(self.app,
                    notification=MockNotification(),
                    tokens=['a' for i in range(MAXIMUM_TOKENS + 1)])
     self.assertEqual(
         str(ex.exception),
         'FCMRequest tokens must contain less than {} tokens'.format(
             MAXIMUM_TOKENS))
예제 #10
0
 def test_fcm_message_data_payload(self):
     platform_config = PlatformConfig(priority=PlatformPriority.HIGH,
                                      collapse_key='collapse_key')
     request = FCMRequest(self.app,
                          notification=MockNotification(
                              data_payload={'some_data': 'some test data'}),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertEqual(message.data, {
         'notification_type': 'verification',
         'some_data': 'some test data'
     })
     self.assertIsNone(message.notification)
     self.assertIsNone(message.android)
     self.assertIsNone(message.apns)
     self.assertIsNone(message.webpush)
     self.assertEqual(message.tokens, ['abc'])
예제 #11
0
 def test_fcm_message_platform_config_override(self):
     platform_config = PlatformConfig(priority=PlatformPriority.HIGH,
                                      collapse_key='collapse_key')
     apns_config = messaging.APNSConfig(
         headers={'apns-collapse-id': 'ios_collapse_key'})
     request = FCMRequest(self.app,
                          notification=MockNotification(
                              platform_config=platform_config,
                              apns_config=apns_config),
                          tokens=['abc'])
     message = request._fcm_message()
     self.assertIsNotNone(message)
     self.assertIsNotNone(message.data)
     self.assertIsNone(message.notification)
     self.assertTrue(isinstance(message.android, messaging.AndroidConfig))
     self.assertTrue(isinstance(message.apns, messaging.APNSConfig))
     self.assertEqual(message.apns.headers,
                      {'apns-collapse-id': 'ios_collapse_key'})
     self.assertTrue(isinstance(message.webpush, messaging.WebpushConfig))
     self.assertEqual(message.webpush.headers, {
         'Topic': 'collapse_key',
         'Urgency': 'high'
     })
     self.assertEqual(message.tokens, ['abc'])
예제 #12
0
    def _send_fcm(cls, clients, notification, backoff_iteration=0):
        # Only send to FCM clients if notifications are enabled
        if not cls._notifications_enabled():
            return 1

        # Only allow so many retries
        backoff_time = 2**backoff_iteration
        if backoff_time > MAXIMUM_BACKOFF:
            return 2

        # Make sure we're only sending to FCM clients
        clients = [
            client for client in clients
            if client.client_type in ClientType.FCM_CLIENTS
        ]

        from models.notifications.requests.fcm_request import FCMRequest, MAXIMUM_TOKENS
        # We can only send to so many FCM clients at a time - send to our clients across several requests
        for subclients in [
                clients[i:i + MAXIMUM_TOKENS]
                for i in range(0, len(clients), MAXIMUM_TOKENS)
        ]:
            fcm_request = FCMRequest(
                firebase_app,
                notification,
                tokens=[client.messaging_id for client in subclients])
            logging.info(str(fcm_request))

            batch_response = fcm_request.send()
            retry_clients = []

            # Handle our failed sends - this might include logging/alerting, removing old clients, or retrying sends
            from firebase_admin.exceptions import InvalidArgumentError, InternalError, UnavailableError
            from firebase_admin.messaging import QuotaExceededError, SenderIdMismatchError, ThirdPartyAuthError, UnregisteredError
            for index, response in enumerate([
                    response for response in batch_response.responses
                    if not response.success
            ]):
                client = subclients[index]
                if isinstance(response.exception, UnregisteredError):
                    logging.info(
                        'Deleting unregistered client with ID: {}'.format(
                            client.messaging_id))
                    MobileClient.delete_for_messaging_id(client.messaging_id)
                elif isinstance(response.exception, SenderIdMismatchError):
                    logging.info(
                        'Deleting mismatched client with ID: {}'.format(
                            client.messaging_id))
                    MobileClient.delete_for_messaging_id(client.messaging_id)
                elif isinstance(response.exception, QuotaExceededError):
                    logging.error('Qutoa exceeded - retrying client...')
                    retry_clients.append(client)
                elif isinstance(response.exception, ThirdPartyAuthError):
                    logging.critical(
                        'Third party error sending to FCM - {}'.format(
                            response.exception))
                elif isinstance(response.exception, InvalidArgumentError):
                    logging.critical(
                        'Invalid argument when sending to FCM - {}'.format(
                            response.exception))
                elif isinstance(response.exception, InternalError):
                    logging.error('Interal FCM error - retrying client...')
                    retry_clients.append(client)
                elif isinstance(response.exception, UnavailableError):
                    logging.error('FCM unavailable - retrying client...')
                    retry_clients.append(client)
                else:
                    debug_string = cls._debug_string(response.exception)
                    logging.error('Unhandled FCM error for {} - {}'.format(
                        client.messaging_id, debug_string))

            if retry_clients:
                # Try again, with exponential backoff
                deferred.defer(cls._send_fcm,
                               retry_clients,
                               notification,
                               backoff_iteration + 1,
                               _countdown=backoff_time,
                               _target='backend-tasks',
                               _queue='push-notifications',
                               _url='/_ah/queue/deferred_notification_send')

        return 0
예제 #13
0
 def test_init_delivery_none(self):
     with self.assertRaises(TypeError):
         FCMRequest(self.app, notification=MockNotification())
예제 #14
0
 def test_init_app(self):
     FCMRequest(self.app, notification=MockNotification(), tokens=['abcd'])
예제 #15
0
 def test_subclass(self):
     request = FCMRequest(self.app, MockNotification(), tokens=['abcd'])
     self.assertTrue(isinstance(request, Request))