diff --git a/firebase_admin/_messaging_encoder.py b/firebase_admin/_messaging_encoder.py index 48a3dd3cd..85072b597 100644 --- a/firebase_admin/_messaging_encoder.py +++ b/firebase_admin/_messaging_encoder.py @@ -160,6 +160,15 @@ def check_analytics_label(cls, label, value): raise ValueError('Malformed {}.'.format(label)) return value + @classmethod + def check_boolean(cls, label, value): + """Checks if the given value is boolean.""" + if value is None: + return None + if not isinstance(value, bool): + raise ValueError('{0} must be a boolean.'.format(label)) + return value + @classmethod def check_datetime(cls, label, value): """Checks if the given value is a datetime.""" @@ -196,6 +205,8 @@ def encode_android(cls, android): 'AndroidConfig.restricted_package_name', android.restricted_package_name), 'ttl': cls.encode_ttl(android.ttl), 'fcm_options': cls.encode_android_fcm_options(android.fcm_options), + 'direct_boot_ok': _Validators.check_boolean( + 'AndroidConfig.direct_boot_ok', android.direct_boot_ok), } result = cls.remove_null_values(result) priority = result.get('priority') diff --git a/firebase_admin/_messaging_utils.py b/firebase_admin/_messaging_utils.py index 64930f1b8..29b8276bc 100644 --- a/firebase_admin/_messaging_utils.py +++ b/firebase_admin/_messaging_utils.py @@ -49,10 +49,12 @@ class AndroidConfig: strings. When specified, overrides any data fields set via ``Message.data``. notification: A ``messaging.AndroidNotification`` to be included in the message (optional). fcm_options: A ``messaging.AndroidFCMOptions`` to be included in the message (optional). + direct_boot_ok: A boolean indicating whether messages will be allowed to be delivered to + the app while the device is in direct boot mode (optional). """ def __init__(self, collapse_key=None, priority=None, ttl=None, restricted_package_name=None, - data=None, notification=None, fcm_options=None): + data=None, notification=None, fcm_options=None, direct_boot_ok=None): self.collapse_key = collapse_key self.priority = priority self.ttl = ttl @@ -60,6 +62,7 @@ def __init__(self, collapse_key=None, priority=None, ttl=None, restricted_packag self.data = data self.notification = notification self.fcm_options = fcm_options + self.direct_boot_ok = direct_boot_ok class AndroidNotification: diff --git a/tests/test_messaging.py b/tests/test_messaging.py index 71bb13eed..5072df6ea 100644 --- a/tests/test_messaging.py +++ b/tests/test_messaging.py @@ -33,6 +33,7 @@ NON_OBJECT_ARGS = [list(), tuple(), dict(), 'foo', 0, 1, True, False] NON_LIST_ARGS = ['', tuple(), dict(), True, False, 1, 0, [1], ['foo', 1]] NON_UINT_ARGS = ['1.23s', list(), tuple(), dict(), -1.23] +NON_BOOL_ARGS = ['', list(), tuple(), dict(), 1, 0, [1], ['foo', 1], {1: 'foo'}, {'foo': 1}] HTTP_ERROR_CODES = { 400: exceptions.InvalidArgumentError, 403: exceptions.PermissionDeniedError, @@ -249,7 +250,8 @@ def test_fcm_options(self): topic='topic', fcm_options=messaging.FCMOptions('message-label'), android=messaging.AndroidConfig( - fcm_options=messaging.AndroidFCMOptions('android-label')), + fcm_options=messaging.AndroidFCMOptions('android-label'), + direct_boot_ok=False), apns=messaging.APNSConfig(fcm_options= messaging.APNSFCMOptions( analytics_label='apns-label', @@ -259,7 +261,8 @@ def test_fcm_options(self): { 'topic': 'topic', 'fcm_options': {'analytics_label': 'message-label'}, - 'android': {'fcm_options': {'analytics_label': 'android-label'}}, + 'android': {'fcm_options': {'analytics_label': 'android-label'}, + 'direct_boot_ok': False}, 'apns': {'fcm_options': {'analytics_label': 'apns-label', 'image': 'https://images.unsplash.com/photo-14944386399' '46-1ebd1d20bf85?fit=crop&w=900&q=60'}}, @@ -317,6 +320,20 @@ def test_invalid_data(self, data): check_encoding(messaging.Message( topic='topic', android=messaging.AndroidConfig(data=data))) + @pytest.mark.parametrize('data', NON_STRING_ARGS) + def test_invalid_analytics_label(self, data): + with pytest.raises(ValueError): + check_encoding(messaging.Message( + topic='topic', android=messaging.AndroidConfig( + fcm_options=messaging.AndroidFCMOptions(analytics_label=data)))) + + @pytest.mark.parametrize('data', NON_BOOL_ARGS) + def test_invalid_direct_boot_ok(self, data): + with pytest.raises(ValueError): + check_encoding(messaging.Message( + topic='topic', android=messaging.AndroidConfig(direct_boot_ok=data))) + + def test_android_config(self): msg = messaging.Message( topic='topic', @@ -326,7 +343,8 @@ def test_android_config(self): priority='high', ttl=123, data={'k1': 'v1', 'k2': 'v2'}, - fcm_options=messaging.AndroidFCMOptions('analytics_label_v1') + fcm_options=messaging.AndroidFCMOptions('analytics_label_v1'), + direct_boot_ok=True, ) ) expected = { @@ -343,6 +361,7 @@ def test_android_config(self): 'fcm_options': { 'analytics_label': 'analytics_label_v1', }, + 'direct_boot_ok': True, }, } check_encoding(msg, expected)