예제 #1
0
    def test_delete_for_messaging_id(self):
        user_id_one = 'user_id_one'
        messaging_id_one = 'messaging_id1'
        messaging_id_two = 'messaging_id2'

        user_id_two = 'user_id_two'
        messaging_id_three = 'messaging_id3'

        for (user_id, messaging_ids) in [(user_id_one, [messaging_id_one, messaging_id_two]), (user_id_two, [messaging_id_three])]:
            for messaging_id in messaging_ids:
                MobileClient(
                    parent=ndb.Key(Account, user_id),
                    user_id=user_id,
                    messaging_id=messaging_id,
                    client_type=ClientType.OS_IOS,
                    device_uuid=messaging_id[::-1],
                    display_name='Phone').put()

        MobileClient.delete_for_messaging_id(messaging_id_one)

        clients_one = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_one').fetch()]
        clients_two = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_two').fetch()]

        self.assertEqual(clients_one, [messaging_id_two])
        self.assertEqual(clients_two, [messaging_id_three])

        MobileClient.delete_for_messaging_id(messaging_id_two)

        clients_one = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_one').fetch()]
        clients_two = [client.messaging_id for client in MobileClient.query(MobileClient.user_id == 'user_id_two').fetch()]

        self.assertEqual(clients_one, [])
        self.assertEqual(clients_two, [messaging_id_three])

        MobileClient.delete_for_messaging_id('does_not_exist')
예제 #2
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