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