Skip to content

Commit

Permalink
⬆️ Migrate to the new HTTP v1 API
Browse files Browse the repository at this point in the history
- Read the paramters for the new HTTP v1 API from the config
- The new version of the library does not support notifying multiple devices at
  the same time. Instead, it supports notifying only one device at a time. The
  return value from the `notify` method is also just the notification ID and
  not the summary with the structure showing the number of successes, number of
  failures, and then the results. Addressed this by creating a backwards compat
  function that has largely the same interface as before
- Renamed the arguments to match the new version
- The new version does not support sending structured data, only a flat K-V
  array, so changed the structure of the message sent for visible notifications
- The new version only supports strings in the K-V array, so changed the notID
  from an int to a string.

Testing done:

Running the following command with both `nrel-commute` and stage, saw visible notifications on both android and iOS

```
- ./e-mission-py.bash bin/push/push_to_users.py -a -t "Testing HTTP v1 API" "The legacy FCM APIs were shutdown starting July 22, 2024. Performance has been steadily degrading since then. This is testing the migration to the new API"
```

Silent push notifications generated a mixture of successful and unsuccessful results

```
./e-mission-py.bash bin/push/silent_ios_push.py -d 3600
{'ios': {'success': 16, 'failure': 14, 'results': ....}
```
  • Loading branch information
shankari committed Sep 12, 2024
1 parent 666a8f5 commit 5a8ca40
Showing 1 changed file with 48 additions and 17 deletions.
65 changes: 48 additions & 17 deletions emission/net/ext_service/push/notify_interface_impl/firebase.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import requests
import copy
import time
import pyfcm

# Our imports
import emission.core.get_database as edb
Expand All @@ -21,7 +22,8 @@ def get_interface(push_config):

class FirebasePush(pni.NotifyInterface):
def __init__(self, push_config):
self.server_auth_token = push_config.get("PUSH_SERVER_AUTH_TOKEN")
self.service_account_file = push_config.get("PUSH_SERVICE_ACCOUNT_FILE")
self.project_id = push_config.get("PUSH_PROJECT_ID")
if "PUSH_APP_PACKAGE_NAME" in push_config:
self.app_package_name = push_config.get("PUSH_APP_PACKAGE_NAME")
else:
Expand All @@ -33,6 +35,30 @@ def get_and_invalidate_entries(self):
# Need to figure out how to do this on firebase
pass

def notify_multiple_devices(self, push_service, fcm_token_list,
notification_title=None, notification_body=None, data_payload=None):
results = {}
n_success = 0
n_failure = 0
for t in fcm_token_list:
trunc_t = t[:10]
try:
result = push_service.notify(fcm_token=t,
notification_title=notification_title,
notification_body=notification_body,
data_payload=data_payload)
results.update({trunc_t: result['name']})
n_success = n_success + 1
print("Successfully sent to %s..." % (trunc_t))
except (pyfcm.errors.FCMNotRegisteredError, pyfcm.errors.InvalidDataError) as e:
results.update({trunc_t: str(e)})
n_failure = n_failure + 1
print("Found error %s while sending to token %s... skipping" % (e, trunc_t))
response = {"success": n_success, "failure": n_failure, "results": results}
print(response)
logging.debug(response)
return response

@staticmethod
def print_dev_flag_warning():
logging.warning("dev flag is ignored for firebase, since the API does not distinguish between production and dev")
Expand Down Expand Up @@ -128,20 +154,22 @@ def send_visible_notification(self, token_map, title, message, json_data, dev=Fa
# convert tokens if necessary
fcm_token_map = self.convert_to_fcm_if_necessary(token_map, dev)

push_service = FCMNotification(api_key=self.server_auth_token)
data_message = {
"data": json_data,
"payload": json_data
}
push_service = FCMNotification(
service_account_file=self.service_account_file,
project_id=self.project_id)
# Send android and iOS messages separately because they have slightly
# different formats
# https://github.com/e-mission/e-mission-server/issues/564#issuecomment-360720598
android_response = push_service.notify_multiple_devices(registration_ids=fcm_token_map["android"],
data_message=data_message)
ios_response = push_service.notify_multiple_devices(registration_ids=fcm_token_map["ios"],
message_body = message,
message_title = title,
data_message=data_message)
android_response = self.notify_multiple_devices(push_service,
fcm_token_map["android"],
notification_body = message,
notification_title = title,
data_payload = json_data)
ios_response = self.notify_multiple_devices(push_service,
fcm_token_map["ios"],
notification_body = message,
notification_title = title,
data_payload = json_data)
combo_response = {"ios": ios_response, "android": android_response}
logging.debug(combo_response)
return combo_response
Expand All @@ -155,20 +183,23 @@ def send_silent_notification(self, token_map, json_data, dev=False):
# multiplying by 10^6 gives us the maximum resolution possible while still
# being not a float. Have to see if that is too big.
# Hopefully we will never send a push notification a millisecond to a single phone
ios_raw_data.update({"notId": int(time.time() * 10**6)})
ios_raw_data.update({"notId": str(int(time.time() * 10**6))})
ios_raw_data.update({"payload": ios_raw_data["notId"]})

push_service = FCMNotification(api_key=self.server_auth_token)
push_service = FCMNotification(
service_account_file=self.service_account_file,
project_id=self.project_id)

# convert tokens if necessary
fcm_token_map = self.convert_to_fcm_if_necessary(token_map, dev)

response = {}
response["ios"] = push_service.notify_multiple_devices(registration_ids=fcm_token_map["ios"],
data_message=ios_raw_data,
content_available=True)
response["ios"] = self.notify_multiple_devices(push_service,
fcm_token_map["ios"], data_payload=ios_raw_data)

response["android"] = {"success": "skipped", "failure": "skipped",
"results": "skipped"}
print(response)
logging.debug(response)
return response

Expand Down

0 comments on commit 5a8ca40

Please sign in to comment.