Fix html5 unsub (#7874)

* Fix #7758 subscription expiration/removal

Removes a subscription after receiving an HTTP 410 response when trying to send a new message.

* Fix tests failing due to additional call

* Fix code style

* Lint
This commit is contained in:
Paulus Schoutsen 2017-06-02 20:56:16 -07:00 committed by GitHub
parent e11ec88482
commit 7d4adbbef5
2 changed files with 24 additions and 6 deletions

View File

@ -115,7 +115,7 @@ def get_service(hass, config, discovery_info=None):
add_manifest_json_key( add_manifest_json_key(
ATTR_GCM_SENDER_ID, config.get(ATTR_GCM_SENDER_ID)) ATTR_GCM_SENDER_ID, config.get(ATTR_GCM_SENDER_ID))
return HTML5NotificationService(gcm_api_key, registrations) return HTML5NotificationService(gcm_api_key, registrations, json_path)
def _load_config(filename): def _load_config(filename):
@ -327,10 +327,11 @@ class HTML5PushCallbackView(HomeAssistantView):
class HTML5NotificationService(BaseNotificationService): class HTML5NotificationService(BaseNotificationService):
"""Implement the notification service for HTML5.""" """Implement the notification service for HTML5."""
def __init__(self, gcm_key, registrations): def __init__(self, gcm_key, registrations, json_path):
"""Initialize the service.""" """Initialize the service."""
self._gcm_key = gcm_key self._gcm_key = gcm_key
self.registrations = registrations self.registrations = registrations
self.registrations_json_path = json_path
@property @property
def targets(self): def targets(self):
@ -383,7 +384,7 @@ class HTML5NotificationService(BaseNotificationService):
if not targets: if not targets:
targets = self.registrations.keys() targets = self.registrations.keys()
for target in targets: for target in list(targets):
info = self.registrations.get(target) info = self.registrations.get(target)
if info is None: if info is None:
_LOGGER.error("%s is not a valid HTML5 push notification" _LOGGER.error("%s is not a valid HTML5 push notification"
@ -399,5 +400,16 @@ class HTML5NotificationService(BaseNotificationService):
jwt_token = jwt.encode(jwt_claims, jwt_secret).decode('utf-8') jwt_token = jwt.encode(jwt_claims, jwt_secret).decode('utf-8')
payload[ATTR_DATA][ATTR_JWT] = jwt_token payload[ATTR_DATA][ATTR_JWT] = jwt_token
WebPusher(info[ATTR_SUBSCRIPTION]).send( response = WebPusher(info[ATTR_SUBSCRIPTION]).send(
json.dumps(payload), gcm_key=self._gcm_key, ttl='86400') json.dumps(payload), gcm_key=self._gcm_key, ttl='86400')
# pylint: disable=no-member
if response.status_code == 410:
_LOGGER.info("Notification channel has expired")
reg = self.registrations.pop(target)
if not _save_config(self.registrations_json_path,
self.registrations):
self.registrations[target] = reg
_LOGGER.error("Error saving registration.")
else:
_LOGGER.info("Configuration saved")

View File

@ -86,10 +86,14 @@ class TestHtml5Notify(object):
service.send_message('Hello', target=['device', 'non_existing'], service.send_message('Hello', target=['device', 'non_existing'],
data={'icon': 'beer.png'}) data={'icon': 'beer.png'})
assert len(mock_wp.mock_calls) == 2 print(mock_wp.mock_calls)
assert len(mock_wp.mock_calls) == 3
# WebPusher constructor # WebPusher constructor
assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1['subscription'] assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1['subscription']
# Third mock_call checks the status_code of the response.
assert mock_wp.mock_calls[2][0] == '().send().status_code.__eq__'
# Call to send # Call to send
payload = json.loads(mock_wp.mock_calls[1][1][0]) payload = json.loads(mock_wp.mock_calls[1][1][0])
@ -376,11 +380,13 @@ class TestHtml5Notify(object):
service.send_message('Hello', target=['device'], service.send_message('Hello', target=['device'],
data={'icon': 'beer.png'}) data={'icon': 'beer.png'})
assert len(mock_wp.mock_calls) == 2 assert len(mock_wp.mock_calls) == 3
# WebPusher constructor # WebPusher constructor
assert mock_wp.mock_calls[0][1][0] == \ assert mock_wp.mock_calls[0][1][0] == \
SUBSCRIPTION_1['subscription'] SUBSCRIPTION_1['subscription']
# Third mock_call checks the status_code of the response.
assert mock_wp.mock_calls[2][0] == '().send().status_code.__eq__'
# Call to send # Call to send
push_payload = json.loads(mock_wp.mock_calls[1][1][0]) push_payload = json.loads(mock_wp.mock_calls[1][1][0])