Skip to content

Commit

Permalink
Response data type changes and timeout option
Browse files Browse the repository at this point in the history
  • Loading branch information
olucurious committed Aug 16, 2017
1 parent 39f112f commit 20be33c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 93 deletions.
17 changes: 6 additions & 11 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Looking for a Django version?
Checkout fcm-django
- Link: https://github.com/xtrinch/fcm-django

Updates
-------
Updates (Breaking Changes)
--------------------------

- MAJOR API UPDATES (DECEMBER 2016): https://github.com/olucurious/PyFCM/releases/tag/1.2.0
- MAJOR UPDATES (AUGUST 2017): https://github.com/olucurious/PyFCM/releases/tag/1.4.0


Quickstart
Expand Down Expand Up @@ -199,21 +199,16 @@ Access response data.
.. code-block:: python
# Response from PyFCM.
response = dict()
# Response from FCM Server.
response['multicast_id'] #Unique ID (number) identifying the multicast message.
response['multicast_ids'] # List of Unique ID (number) identifying the multicast message.
response['success'] #Number of messages that were processed without an error.
response['failure'] #Number of messages that could not be processed.
response['canonical_ids'] #Number of results that contain a canonical registration token.
response['results'] #Array of objects representing the status of the messages processed.
# For notify_multiple_devices
result = [{response dict},...] #list of response dicts is returned
# For notify_single_device or notify_topic_subscribers
result = {response dict} #single response dict is returned
# The response objects are listed in the same order as the request (i.e., for each registration ID in the request,
# its response is listed in the same index in the response).
# message_id: String specifying a unique ID for each successfully processed message.
# registration_id: Optional string specifying the canonical registration token for the client app that the message
# was processed and sent to. Sender should use this value as the registration token for future requests. Otherwise,
Expand Down
2 changes: 1 addition & 1 deletion pyfcm/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
__summary__ = 'Python client for FCM - Firebase Cloud Messaging (Android & iOS)..'
__url__ = 'https://github.com/olucurious/pyfcm'

__version__ = '1.3.2'
__version__ = '1.4.0'

__install_requires__ = ['requests', 'requests-toolbelt']

Expand Down
57 changes: 34 additions & 23 deletions pyfcm/baseapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from .errors import *


class BaseAPI(object):
"""
Base class for the pyfcm API wrapper for FCM
Expand Down Expand Up @@ -141,10 +142,10 @@ def parse_payload(self,
fcm_payload['notification'] = {}
if message_icon:
fcm_payload['notification']['icon'] = message_icon
#If body is present, use it
# If body is present, use it
if message_body:
fcm_payload['notification']['body'] = message_body
#Else use body_loc_key and body_loc_args for body
# Else use body_loc_key and body_loc_args for body
else:
if body_loc_key:
fcm_payload['notification']['body_loc_key'] = body_loc_key
Expand All @@ -153,10 +154,10 @@ def parse_payload(self,
fcm_payload['notification']['body_loc_args'] = body_loc_args
else:
raise InvalidDataError('body_loc_args should be an array')
#If title is present, use it
# If title is present, use it
if message_title:
fcm_payload['notification']['title'] = message_title
#Else use title_loc_key and title_loc_args for title
# Else use title_loc_key and title_loc_args for title
else:
if title_loc_key:
fcm_payload['notification']['title_loc_key'] = title_loc_key
Expand Down Expand Up @@ -188,49 +189,57 @@ def parse_payload(self,

return self.json_dumps(fcm_payload)

def do_request(self, payload):
def do_request(self, payload, timeout):
if self.FCM_REQ_PROXIES:
response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload,
proxies=self.FCM_REQ_PROXIES)
proxies=self.FCM_REQ_PROXIES, timeout=timeout)
else:
response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload)
response = requests.post(self.FCM_END_POINT, headers=self.request_headers(), data=payload, timeout=timeout)
if 'Retry-After' in response.headers and int(response.headers['Retry-After']) > 0:
sleep_time = int(response.headers['Retry-After'])
time.sleep(sleep_time)
return self.do_request(payload)
return self.do_request(payload, timeout)
return response

def send_request(self, payloads=None):
def send_request(self, payloads=None, timeout=None):
self.send_request_responses = []
for payload in payloads:
response = self.do_request(payload)
response = self.do_request(payload, timeout)
self.send_request_responses.append(response)

def clean_registration_ids(self, registration_ids=[]):
"""Return list of active IDS from the list of registration_ids
"""
valid_registration_ids = []
for registration_id in registration_ids:
details = requests.get('https://iid.googleapis.com/iid/info/'+registration_id,
details = requests.get('https://iid.googleapis.com/iid/info/' + registration_id,
headers=self.request_headers(),
params={'details':'true'})
params={'details': 'true'})
if details.status_code == 200:
valid_registration_ids.append(registration_id)
return valid_registration_ids

def parse_responses(self):
response_list = list()
"""
Returns a python dict of multicast_ids(list), success(int), failure(int), canonical_ids(int), results(list) and optional topic_message_id(str but None by default)
"""
response_dict = {
'multicast_ids': list(),
'success': 0,
'failure': 0,
'canonical_ids': 0,
'results': list(),
'topic_message_id': None
}

for response in self.send_request_responses:
if response.status_code == 200:
"""
Parses the json response sent back by the
server and tries to get out the important return variables
Returns a python dict of multicast_id(long), success(int), failure(int), canonical_ids(int), results(list)
"""
if 'content-length' in response.headers and int(response.headers['content-length']) <= 0:
response_list.append({})
FCMServerError("FCM server connection error, the response is empty")
else:
parsed_response = response.json()

Expand All @@ -242,15 +251,17 @@ def parse_responses(self):
message_id = parsed_response.get('message_id', None) # for topic messages
if message_id:
success = 1
response_list.append({'multicast_id': multicast_id,
'success': success,
'failure': failure,
'canonical_ids': canonical_ids,
'results': results})
if multicast_id:
response_dict['multicast_id'].append(multicast_id)
response_dict['success'] += success
response_dict['failure'] += failure
response_dict['canonical_ids'] += canonical_ids
response_dict['results'].extend(results)
response_dict['topic_message_id'] = message_id
elif response.status_code == 401:
raise AuthenticationError("There was an error authenticating the sender account")
elif response.status_code == 400:
raise InternalPackageError(response.text)
else:
raise FCMServerError("FCM server is temporarily unavailable")
return response_list
return response_dict
95 changes: 37 additions & 58 deletions pyfcm/fcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def notify_single_device(self,
title_loc_key=None,
title_loc_args=None,
content_available=None,
timeout=5,
extra_kwargs={}):

"""
Expand Down Expand Up @@ -54,6 +55,7 @@ def notify_single_device(self,
receive the message. Defaults to ``None``.
dry_run (bool, optional): If ``True`` no message will be sent but
request will be tested.
timeout (int, optional): set time limit for the request
Returns:
:dict:`multicast_id(long), success(int), failure(int), canonical_ids(int), results(list)`:
Response from FCM server.
Expand Down Expand Up @@ -89,8 +91,8 @@ def notify_single_device(self,
content_available=content_available,
**extra_kwargs)

self.send_request([payload])
return self.parse_responses()[-1:][0]
self.send_request([payload], timeout)
return self.parse_responses()

def notify_multiple_devices(self,
registration_ids=None,
Expand All @@ -115,6 +117,7 @@ def notify_multiple_devices(self,
title_loc_key=None,
title_loc_args=None,
content_available=None,
timeout=5,
extra_kwargs={}):

"""
Expand Down Expand Up @@ -154,59 +157,34 @@ def notify_multiple_devices(self,
InvalidDataError: Invalid data provided
InternalPackageError: JSON parsing error, mostly from changes in the response of FCM, create a new github issue to resolve it.
"""
if len(registration_ids) > self.FCM_MAX_RECIPIENTS:
payloads = list()
registration_id_chunks = self.registration_id_chunks(registration_ids)
for registration_ids in registration_id_chunks:
# appends a payload with a chunk of registration ids here
payloads.append(self.parse_payload(registration_ids=registration_ids,
message_body=message_body,
message_title=message_title,
message_icon=message_icon,
sound=sound,
condition=condition,
collapse_key=collapse_key,
delay_while_idle=delay_while_idle,
time_to_live=time_to_live,
restricted_package_name=restricted_package_name,
low_priority=low_priority,
dry_run=dry_run, data_message=data_message,
click_action=click_action,
badge=badge,
color=color,
tag=tag,
body_loc_key=body_loc_key,
body_loc_args=body_loc_args,
title_loc_key=title_loc_key,
title_loc_args=title_loc_args,
content_available=content_available,
**extra_kwargs))
self.send_request(payloads)
return self.parse_responses()
else:
payload = self.parse_payload(registration_ids=registration_ids,
message_body=message_body,
message_title=message_title,
message_icon=message_icon,
sound=sound,
condition=condition,
collapse_key=collapse_key,
delay_while_idle=delay_while_idle,
time_to_live=time_to_live,
restricted_package_name=restricted_package_name,
low_priority=low_priority,
dry_run=dry_run, data_message=data_message, click_action=click_action,
badge=badge,
color=color,
tag=tag,
body_loc_key=body_loc_key,
body_loc_args=body_loc_args,
title_loc_key=title_loc_key,
title_loc_args=title_loc_args,
content_available=content_available,
**extra_kwargs)
self.send_request([payload])
return self.parse_responses()
payloads = list()
registration_id_chunks = self.registration_id_chunks(registration_ids)
for registration_ids in registration_id_chunks:
# appends a payload with a chunk of registration ids here
payloads.append(self.parse_payload(registration_ids=registration_ids,
message_body=message_body,
message_title=message_title,
message_icon=message_icon,
sound=sound,
condition=condition,
collapse_key=collapse_key,
delay_while_idle=delay_while_idle,
time_to_live=time_to_live,
restricted_package_name=restricted_package_name,
low_priority=low_priority,
dry_run=dry_run, data_message=data_message,
click_action=click_action,
badge=badge,
color=color,
tag=tag,
body_loc_key=body_loc_key,
body_loc_args=body_loc_args,
title_loc_key=title_loc_key,
title_loc_args=title_loc_args,
content_available=content_available,
**extra_kwargs))
self.send_request(payloads, timeout)
return self.parse_responses()

def notify_topic_subscribers(self,
topic_name=None,
Expand All @@ -231,10 +209,11 @@ def notify_topic_subscribers(self,
title_loc_key=None,
title_loc_args=None,
content_available=None,
timeout=5,
extra_kwargs={}):

"""
Sends push notification to multiple devices subscribe to a topic
Sends push notification to multiple devices subscribed to a topic
Args:
topic_name (topic_name): Name of the topic to deliver messages to
Expand Down Expand Up @@ -292,5 +271,5 @@ def notify_topic_subscribers(self,
title_loc_args=title_loc_args,
content_available=content_available,
**extra_kwargs)
self.send_request([payload])
return self.parse_responses()[-1:][0]
self.send_request([payload], timeout)
return self.parse_responses()

0 comments on commit 20be33c

Please sign in to comment.