feat(fcm): Enable fid and deprecate token for Send API#951
feat(fcm): Enable fid and deprecate token for Send API#951yvonnep165 wants to merge 8 commits intomainfrom
fid and deprecate token for Send API#951Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for Firebase Installation IDs (fid and fids) in the Message and MulticastMessage classes, marking the existing token and tokens fields as deprecated. Feedback highlights a critical backward-compatibility issue where changing the MulticastMessage constructor signature breaks positional arguments for existing users. Additionally, it is recommended to extract duplicated logic for converting multicast messages into individual message objects into a shared helper function to improve maintainability.
| def __init__(self, fids=None, tokens=None, data=None, notification=None, android=None, webpush=None, apns=None, | ||
| fcm_options=None): | ||
| _Validators.check_string_list('MulticastMessage.tokens', tokens) | ||
| if len(tokens) > 500: | ||
| raise ValueError('MulticastMessage.tokens must not contain more than 500 tokens.') | ||
| self.tokens = tokens | ||
| if tokens is not None: | ||
| warnings.warn( | ||
| "Deprecated. Use 'fids' instead.", | ||
| DeprecationWarning, | ||
| stacklevel=2 | ||
| ) | ||
|
|
||
| if (tokens is None and fids is None) or (tokens is not None and fids is not None): | ||
| raise ValueError("Must specify either 'tokens' or 'fids'.") | ||
|
|
||
| if tokens is not None: | ||
| _Validators.check_string_list('MulticastMessage.tokens', tokens) | ||
| if len(tokens) > 500: | ||
| raise ValueError('MulticastMessage.tokens must not contain more than 500 tokens.') | ||
| self.tokens = tokens | ||
| self.fids = None | ||
| else: | ||
| _Validators.check_string_list('MulticastMessage.fids', fids) | ||
| if len(fids) > 500: | ||
| raise ValueError('MulticastMessage.fids must not contain more than 500 fids.') | ||
| self.fids = fids | ||
| self.tokens = None |
There was a problem hiding this comment.
The change to the MulticastMessage.__init__ method signature introduces a significant backward-incompatible change. The new signature is def __init__(self, fids=None, tokens=None, ...) where previously it was def __init__(self, tokens, ...). This breaks calls that use positional arguments, as MulticastMessage(['my-token']) will now silently and incorrectly interpret ['my-token'] as a list of fids.
To avoid this breaking change, I suggest reverting the argument order and adjusting the logic to handle the mutual exclusivity of tokens and fids. The tokens parameter should be made optional, and fids can be added at the end. This ensures backward compatibility for existing users.
def __init__(self, tokens=None, data=None, notification=None, android=None, webpush=None, apns=None,
fcm_options=None, fids=None):
if tokens is not None:
warnings.warn(
"Deprecated. Use 'fids' instead.",
DeprecationWarning,
stacklevel=2
)
if (tokens is not None) + (fids is not None) != 1:
raise ValueError("Must specify exactly one of 'tokens' or 'fids'.")
if tokens is not None:
_Validators.check_string_list('MulticastMessage.tokens', tokens)
if len(tokens) > 500:
raise ValueError('MulticastMessage.tokens must not contain more than 500 tokens.')
self.tokens = tokens
self.fids = None
else:
_Validators.check_string_list('MulticastMessage.fids', fids)
if len(fids) > 500:
raise ValueError('MulticastMessage.fids must not contain more than 500 fids.')
self.fids = fids
self.tokens = None| if multicast_message.tokens is not None: | ||
| with warnings.catch_warnings(): | ||
| warnings.simplefilter("ignore", DeprecationWarning) | ||
| messages = [Message( | ||
| data=multicast_message.data, | ||
| notification=multicast_message.notification, | ||
| android=multicast_message.android, | ||
| webpush=multicast_message.webpush, | ||
| apns=multicast_message.apns, | ||
| fcm_options=multicast_message.fcm_options, | ||
| token=token | ||
| ) for token in multicast_message.tokens] | ||
| else: | ||
| messages = [Message( | ||
| data=multicast_message.data, | ||
| notification=multicast_message.notification, | ||
| android=multicast_message.android, | ||
| webpush=multicast_message.webpush, | ||
| apns=multicast_message.apns, | ||
| fcm_options=multicast_message.fcm_options, | ||
| fid=fid | ||
| ) for fid in multicast_message.fids] |
There was a problem hiding this comment.
This block of logic for converting a MulticastMessage into a list of Message objects is duplicated in send_each_for_multicast (lines 245-266). To improve maintainability and follow the DRY (Don't Repeat Yourself) principle, consider extracting this logic into a private helper function that can be called from both send_each_for_multicast_async and send_each_for_multicast.
…y positional arguments and add a unit test for it
lahirumaramba
left a comment
There was a problem hiding this comment.
Thank you! Looks great! :)
Added a few comments.
|
|
||
| def __init__(self, data=None, notification=None, android=None, webpush=None, apns=None, | ||
| fcm_options=None, token=None, topic=None, condition=None): | ||
| fcm_options=None, fid=None, token=None, topic=None, condition=None): |
There was a problem hiding this comment.
Let's move fid=None to end of the list to remove any potential backward compatibility issues if someone is currently passing arguments positionally rather than by keyword.
def __init__(self, data=None, notification=None, android=None, webpush=None, apns=None,
fcm_options=None, token=None, topic=None, condition=None, fid=None):| fcm_options=None, fid=None, token=None, topic=None, condition=None): | ||
| if token is not None: | ||
| warnings.warn( | ||
| "Deprecated. Use 'fid' instead.", |
There was a problem hiding this comment.
Let's change this to Message.token is deprecated. Use fid instead.
| "Deprecated. Use 'fid' instead.", | |
| "Message.token is deprecated. Use fid instead.", |
| webpush=None, apns=None, fcm_options=None): | ||
| if tokens is not None: | ||
| warnings.warn( | ||
| "Deprecated. Use 'fids' instead.", |
There was a problem hiding this comment.
| "Deprecated. Use 'fids' instead.", | |
| "MulticastMessage.tokens is deprecated. Use fids instead.", |
| fcm_options: An instance of ``messaging.FCMOptions`` (optional). | ||
| token: The registration token of the device to which the message should be sent (optional). | ||
| fid: The Firebase installation ID of an FCM registered app instance to which the | ||
| message should be sent (optional) |
|
|
||
| Args: | ||
| tokens: A list of registration tokens of targeted devices. | ||
| fids: A list of Firebase Installation IDs of targeted app instances (optional) |
| assert all(r.success for r in batch_response.responses) | ||
| assert not any(r.exception for r in batch_response.responses) | ||
|
|
||
| def test_send_each_for_multicast_fids(self): |
There was a problem hiding this comment.
do we have any tests for send_each_for_multicast_async?
There was a problem hiding this comment.
We didn't have unit tests for send_each_for_multicast_async previously but only included it in integration tests. Added some unit tests with respx to mock raw HTTP/2 concurrent POST requests to Google's servers
…ddress other review comments
This change deprecates
token(in Message) andtokens(in MulticastMessage) as message targets, transitioning support tofid(Firebase Installation ID) andfidsas the primary targets instead.The active multicast helper functions (
send_each_for_multicastandsend_each_for_multicast_async) are updated to support both target types. These helpers leverage Python'swarnings.catch_warnings()to suppress duplicate internal warning logs during loop instantiation, ensuring a clean developer experience by raising only a single deprecation warning at the point of object construction.