From dbbc467b381b1d5db93022fc2b4be5af9c9764ff Mon Sep 17 00:00:00 2001 From: Hugo Herter Date: Wed, 24 May 2017 17:29:08 -0700 Subject: [PATCH 1/4] Fix #7758 subscription expiration/removal Removes a subscription after receiving an HTTP 410 response when trying to send a new message. --- homeassistant/components/notify/html5.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index f656fc0a3026e6..95ca4a81dd4cea 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -115,7 +115,7 @@ def get_service(hass, config, discovery_info=None): add_manifest_json_key( 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): @@ -327,10 +327,11 @@ def post(self, request): class HTML5NotificationService(BaseNotificationService): """Implement the notification service for HTML5.""" - def __init__(self, gcm_key, registrations): + def __init__(self, gcm_key, registrations, json_path): """Initialize the service.""" self._gcm_key = gcm_key self.registrations = registrations + self.registrations_json_path = json_path @property def targets(self): @@ -383,7 +384,7 @@ def send_message(self, message="", **kwargs): if not targets: targets = self.registrations.keys() - for target in targets: + for target in list(targets): info = self.registrations.get(target) if info is None: _LOGGER.error("%s is not a valid HTML5 push notification" @@ -399,5 +400,14 @@ def send_message(self, message="", **kwargs): jwt_token = jwt.encode(jwt_claims, jwt_secret).decode('utf-8') 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') + + 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") \ No newline at end of file From ba3677f7f8cc7b4f03aeca48bc205fc6223499ad Mon Sep 17 00:00:00 2001 From: Hugo Herter Date: Wed, 24 May 2017 17:51:29 -0700 Subject: [PATCH 2/4] Fix tests failing due to additional call --- tests/components/notify/test_html5.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/components/notify/test_html5.py b/tests/components/notify/test_html5.py index 8d27a11e0943de..652a70b86840a9 100644 --- a/tests/components/notify/test_html5.py +++ b/tests/components/notify/test_html5.py @@ -86,10 +86,15 @@ def test_sending_message(self, mock_wp): service.send_message('Hello', target=['device', 'non_existing'], 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 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 payload = json.loads(mock_wp.mock_calls[1][1][0]) @@ -376,11 +381,13 @@ def test_callback_view_with_jwt(self, loop, test_client): service.send_message('Hello', target=['device'], data={'icon': 'beer.png'}) - assert len(mock_wp.mock_calls) == 2 + assert len(mock_wp.mock_calls) == 3 # WebPusher constructor 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 push_payload = json.loads(mock_wp.mock_calls[1][1][0]) From fbecd6942ebc26514beb7c8dba309bbe12a535ca Mon Sep 17 00:00:00 2001 From: Hugo Herter Date: Wed, 24 May 2017 17:57:09 -0700 Subject: [PATCH 3/4] Fix code style --- homeassistant/components/notify/html5.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index 95ca4a81dd4cea..7ce2f6fa894448 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -406,8 +406,9 @@ def send_message(self, message="", **kwargs): 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): + if not _save_config(self.registrations_json_path, + self.registrations): self.registrations[target] = reg _LOGGER.error("Error saving registration.") else: - _LOGGER.info("Configuration saved") \ No newline at end of file + _LOGGER.info("Configuration saved") From ea75b7a4d1d4a847c2302ca289963905856f14ed Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 2 Jun 2017 20:43:52 -0700 Subject: [PATCH 4/4] Lint --- homeassistant/components/notify/html5.py | 3 ++- tests/components/notify/test_html5.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index 7ce2f6fa894448..495fc8a3c6f1af 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -403,7 +403,8 @@ def send_message(self, message="", **kwargs): response = WebPusher(info[ATTR_SUBSCRIPTION]).send( json.dumps(payload), gcm_key=self._gcm_key, ttl='86400') - if (response.status_code == 410): + # 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, diff --git a/tests/components/notify/test_html5.py b/tests/components/notify/test_html5.py index 652a70b86840a9..ff1076d1eeddf1 100644 --- a/tests/components/notify/test_html5.py +++ b/tests/components/notify/test_html5.py @@ -90,7 +90,6 @@ def test_sending_message(self, mock_wp): assert len(mock_wp.mock_calls) == 3 - # WebPusher constructor assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1['subscription'] # Third mock_call checks the status_code of the response.