From 7ebc46e74e546684824cdc2e862b03eb3f3da7ac Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Fri, 26 Jan 2018 18:23:32 -0800 Subject: [PATCH 1/2] Add support for converting to sandbox tokens on firebase Although firebase does not support sandbox v/s production for the send API, they do support it for the import/conversion API. And if you use `sandbox=False` while trying to convert a token from a debug build, the send will fail with "Not registered" or "Invalid Token". Since we already have a dev flag, let's hook it up to this. https://github.com/e-mission/e-mission-server/issues/564#issuecomment-360725717 https://github.com/e-mission/e-mission-server/issues/564#issuecomment-360728695 and make it easier to test --- .../push/notify_interface_impl/firebase.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/emission/net/ext_service/push/notify_interface_impl/firebase.py b/emission/net/ext_service/push/notify_interface_impl/firebase.py index f7baf344f..746beaabc 100644 --- a/emission/net/ext_service/push/notify_interface_impl/firebase.py +++ b/emission/net/ext_service/push/notify_interface_impl/firebase.py @@ -48,12 +48,12 @@ def map_existing_fcm_tokens(self, token_list): unmapped_token_list.append(token) return (mapped_token_list, unmapped_token_list) - def retrieve_fcm_tokens(self, token_list): + def retrieve_fcm_tokens(self, token_list, dev): importHeaders = {"Authorization": "key=%s" % self.server_auth_token, "Content-Type": "application/json"} importMessage = { "application": "edu.berkeley.eecs.emission", - "sandbox": False, + "sandbox": dev, "apns_tokens":token_list } logging.debug("About to send message %s" % importMessage) @@ -80,9 +80,9 @@ def process_fcm_token_result(self, token_list, importedResultJSON): ret_list.append(token_list[i]) return ret_list - def convert_to_fcm_if_necessary(self, token_list): + def convert_to_fcm_if_necessary(self, token_list, dev): (mapped_token_list, unmapped_token_list) = self.map_existing_fcm_tokens(token_list) - importedResultJSON = self.retrieve_fcm_tokens(unmapped_token_list) + importedResultJSON = self.retrieve_fcm_tokens(unmapped_token_list, dev) newly_mapped_token_list = self.process_fcm_token_result(token_list, importedResultJSON) return mapped_token_list + newly_mapped_token_list @@ -91,10 +91,8 @@ def send_visible_notification(self, token_list, title, message, json_data, dev=F logging.info("len(token_list) == 0, early return to save api calls") return - FirebasePush.print_dev_flag_warning() - # convert tokens if necessary - fcm_token_list = self.convert_to_fcm_if_necessary(token_list) + fcm_token_list = self.convert_to_fcm_if_necessary(token_list, dev) push_service = FCMNotification(api_key=self.server_auth_token) data_message = { @@ -120,9 +118,8 @@ def send_silent_notification(self, token_list, json_data, dev=False): push_service = FCMNotification(api_key=self.server_auth_token) - FirebasePush.print_dev_flag_warning() # convert tokens if necessary - fcm_token_list = self.convert_to_fcm_if_necessary(token_list) + fcm_token_list = self.convert_to_fcm_if_necessary(token_list, dev) response = push_service.notify_multiple_devices(registration_ids=fcm_token_list, data_message=ios_raw_data, From 8e306fb9be532d995cabc4c28e6907fd95dcacb4 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sat, 27 Jan 2018 09:44:05 -0800 Subject: [PATCH 2/2] Format ios and android messages differently Message and title need to be provided for iOS MUST NOT be provided for android https://github.com/e-mission/e-mission-server/issues/564 --- .../push/notify_interface_impl/firebase.py | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/emission/net/ext_service/push/notify_interface_impl/firebase.py b/emission/net/ext_service/push/notify_interface_impl/firebase.py index 746beaabc..b63add32e 100644 --- a/emission/net/ext_service/push/notify_interface_impl/firebase.py +++ b/emission/net/ext_service/push/notify_interface_impl/firebase.py @@ -33,7 +33,8 @@ def print_dev_flag_warning(): logging.warning("https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages") def map_existing_fcm_tokens(self, token_list): - mapped_token_list = [] + mapped_token_map = {"ios": [], + "android": []} unmapped_token_list = [] for token in token_list: @@ -41,12 +42,13 @@ def map_existing_fcm_tokens(self, token_list): if existing_mapping is not None: assert(existing_mapping["native_token"] == token) mapped_token = existing_mapping["mapped_token"] - logging.debug("mapped %s -> %s" % (token, mapped_token)) - mapped_token_list.append(mapped_token) + mapped_platform = existing_mapping["platform"] + logging.debug("%s: mapped %s -> %s" % (mapped_platform, token, mapped_token)) + mapped_token_map[mapped_platform].append(mapped_token) else: logging.debug("No mapping found for token %s, need to query from database" % token) unmapped_token_list.append(token) - return (mapped_token_list, unmapped_token_list) + return (mapped_token_map, unmapped_token_list) def retrieve_fcm_tokens(self, token_list, dev): importHeaders = {"Authorization": "key=%s" % self.server_auth_token, @@ -63,28 +65,34 @@ def retrieve_fcm_tokens(self, token_list, dev): return importedResultJSON def process_fcm_token_result(self, token_list, importedResultJSON): - ret_list = [] + ret_map = {"ios": [], + "android": []} if "results" in importedResultJSON: importedResult = importedResultJSON["results"] for i, result in enumerate(importedResult): if result["status"] == "OK" and "registration_token" in result: - ret_list.append(result["registration_token"]) + ret_map["ios"].append(result["registration_token"]) logging.debug("Found firebase mapping from %s -> %s at index %d"% (result["apns_token"], result["registration_token"], i)); edb.get_push_token_mapping_db().insert({"native_token": result["apns_token"], + "platform": "ios", "mapped_token": result["registration_token"]}) else: logging.debug("Must already be android token, leave it unchanged"); # TODO: Determine whether to store a mapping here or not depending on what the result # for an android token is - ret_list.append(token_list[i]) - return ret_list + ret_map["android"].append(token_list[i]) + return ret_map def convert_to_fcm_if_necessary(self, token_list, dev): - (mapped_token_list, unmapped_token_list) = self.map_existing_fcm_tokens(token_list) + (mapped_token_map, unmapped_token_list) = self.map_existing_fcm_tokens(token_list) importedResultJSON = self.retrieve_fcm_tokens(unmapped_token_list, dev) - newly_mapped_token_list = self.process_fcm_token_result(token_list, importedResultJSON) - return mapped_token_list + newly_mapped_token_list + newly_mapped_token_map = self.process_fcm_token_result(token_list, importedResultJSON) + combo_token_map = {"ios": [], + "android": []} + combo_token_map["ios"] = mapped_token_map["ios"] + newly_mapped_token_map["ios"] + combo_token_map["android"] = mapped_token_map["android"] + newly_mapped_token_map["android"] + return combo_token_map def send_visible_notification(self, token_list, title, message, json_data, dev=False): if len(token_list) == 0: @@ -92,17 +100,25 @@ def send_visible_notification(self, token_list, title, message, json_data, dev=F return # convert tokens if necessary - fcm_token_list = self.convert_to_fcm_if_necessary(token_list, dev) + fcm_token_map = self.convert_to_fcm_if_necessary(token_list, dev) push_service = FCMNotification(api_key=self.server_auth_token) data_message = { "data": json_data, "payload": json_data } - response = push_service.notify_multiple_devices(registration_ids=fcm_token_list, + # 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) - logging.debug(response) - return response + ios_response = push_service.notify_multiple_devices(registration_ids=fcm_token_map["ios"], + message_body = message, + message_title = title, + data_message=data_message) + combo_response = {"ios": ios_response, "android": android_response} + logging.debug(combo_response) + return combo_response def send_silent_notification(self, token_list, json_data, dev=False): if len(token_list) == 0: @@ -128,6 +144,10 @@ def send_silent_notification(self, token_list, json_data, dev=False): return response def display_response(self, response): - response_json = response - logging.debug("firebase push result: success %s failure %s results %s" % - (response_json["success"], response_json["failure"], response_json["results"])) + response_ios = response["ios"] + response_android = response['android'] + + logging.debug("firebase push result for ios: success %s failure %s results %s" % + (response_ios["success"], response_ios["failure"], response_ios["results"])) + logging.debug("firebase push result for android: success %s failure %s results %s" % + (response_android["success"], response_android["failure"], response_android["results"]))