diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f65f2b45..cc18ad3a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Unreleased -- +- [changed] Improved error handling in FCM by mapping more server-side + errors to client-side error codes. # v2.9.0 diff --git a/firebase_admin/messaging.py b/firebase_admin/messaging.py index 0ba29c782..5de86e7f3 100644 --- a/firebase_admin/messaging.py +++ b/firebase_admin/messaging.py @@ -716,11 +716,18 @@ class _MessagingService(object): INTERNAL_ERROR = 'internal-error' UNKNOWN_ERROR = 'unknown-error' FCM_ERROR_CODES = { - 'APNS_AUTH_ERROR': 'authentication-error', + # FCM v1 canonical error codes + 'NOT_FOUND': 'registration-token-not-registered', + 'PERMISSION_DENIED': 'mismatched-credential', + 'RESOURCE_EXHAUSTED': 'message-rate-exceeded', + 'UNAUTHENTICATED': 'invalid-apns-credentials', + + # FCM v1 new error codes + 'APNS_AUTH_ERROR': 'invalid-apns-credentials', 'INTERNAL': INTERNAL_ERROR, 'INVALID_ARGUMENT': 'invalid-argument', 'QUOTA_EXCEEDED': 'message-rate-exceeded', - 'SENDER_ID_MISMATCH': 'authentication-error', + 'SENDER_ID_MISMATCH': 'mismatched-credential', 'UNAVAILABLE': 'server-unavailable', 'UNREGISTERED': 'registration-token-not-registered', } diff --git a/tests/test_messaging.py b/tests/test_messaging.py index 56380a19d..6fcdde3a9 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -855,6 +855,26 @@ def test_send_detailed_error(self, status): body = {'message': messaging._MessagingService.JSON_ENCODER.default(msg)} assert json.loads(recorder[0].body.decode()) == body + @pytest.mark.parametrize('status', HTTP_ERRORS) + def test_send_canonical_error_code(self, status): + payload = json.dumps({ + 'error': { + 'status': 'NOT_FOUND', + 'message': 'test error' + } + }) + _, recorder = self._instrument_messaging_service(status=status, payload=payload) + msg = messaging.Message(topic='foo') + with pytest.raises(messaging.ApiCallError) as excinfo: + messaging.send(msg) + assert str(excinfo.value) == 'test error' + assert str(excinfo.value.code) == 'registration-token-not-registered' + assert len(recorder) == 1 + assert recorder[0].method == 'POST' + assert recorder[0].url == self._get_url('explicit-project-id') + body = {'message': messaging._MessagingService.JSON_ENCODER.default(msg)} + assert json.loads(recorder[0].body.decode()) == body + class TestTopicManagement(object):