mirror of https://github.com/caronc/apprise
				
				
				
			Improved exception handling of JSON responses
							parent
							
								
									7b4221039e
								
							
						
					
					
						commit
						4692941efa
					
				|  | @ -125,8 +125,8 @@ uptime | apprise \ | ||||||
| ### Configuration Files | ### Configuration Files | ||||||
| No one wants to put there credentials out for everyone to see on the command line.  No problem *apprise* also supports configuration files.  It can handle both a specific [YAML format](https://github.com/caronc/apprise/wiki/config_yaml) or a very simple [TEXT format](https://github.com/caronc/apprise/wiki/config_text). You can also pull these configuration files via an HTTP query too! You can read more about the expected structure of the configuration files [here](https://github.com/caronc/apprise/wiki/config). | No one wants to put there credentials out for everyone to see on the command line.  No problem *apprise* also supports configuration files.  It can handle both a specific [YAML format](https://github.com/caronc/apprise/wiki/config_yaml) or a very simple [TEXT format](https://github.com/caronc/apprise/wiki/config_text). You can also pull these configuration files via an HTTP query too! You can read more about the expected structure of the configuration files [here](https://github.com/caronc/apprise/wiki/config). | ||||||
| ```bash | ```bash | ||||||
| # By default now if no url or configuration is specified aprise will | # By default if no url or configuration is specified aprise will attempt to load | ||||||
| # peek for this data in: | # configuration files (if present): | ||||||
| #  ~/.apprise | #  ~/.apprise | ||||||
| #  ~/.apprise.yml | #  ~/.apprise.yml | ||||||
| #  ~/.config/apprise | #  ~/.config/apprise | ||||||
|  |  | ||||||
|  | @ -318,11 +318,13 @@ class NotifyD7Networks(NotifyBase): | ||||||
|                         json_response = loads(r.content) |                         json_response = loads(r.content) | ||||||
|                         status_str = json_response.get('message', status_str) |                         status_str = json_response.get('message', status_str) | ||||||
| 
 | 
 | ||||||
|                     except (AttributeError, ValueError): |                     except (AttributeError, TypeError, ValueError): | ||||||
|                         # could not parse JSON response... just use the status |                         # ValueError = r.content is Unparsable | ||||||
|                         # we already have. |                         # TypeError = r.content is None | ||||||
|  |                         # AttributeError = r is None | ||||||
| 
 | 
 | ||||||
|                         # AttributeError means r.content was None |                         # We could not parse JSON response. | ||||||
|  |                         # We will just use the status we already have. | ||||||
|                         pass |                         pass | ||||||
| 
 | 
 | ||||||
|                     self.logger.warning( |                     self.logger.warning( | ||||||
|  | @ -350,9 +352,13 @@ class NotifyD7Networks(NotifyBase): | ||||||
|                             count = int(json_response.get( |                             count = int(json_response.get( | ||||||
|                                 'data', {}).get('messageCount', -1)) |                                 'data', {}).get('messageCount', -1)) | ||||||
| 
 | 
 | ||||||
|                         except (AttributeError, ValueError, TypeError): |                         except (AttributeError, TypeError, ValueError): | ||||||
|                             # could not parse JSON response... just assume |                             # ValueError = r.content is Unparsable | ||||||
|                             # that our delivery is okay for now |                             # TypeError = r.content is None | ||||||
|  |                             # AttributeError = r is None | ||||||
|  | 
 | ||||||
|  |                             # We could not parse JSON response. Assume that | ||||||
|  |                             # our delivery is okay for now. | ||||||
|                             pass |                             pass | ||||||
| 
 | 
 | ||||||
|                         if count != len(self.targets): |                         if count != len(self.targets): | ||||||
|  |  | ||||||
|  | @ -240,9 +240,12 @@ class NotifyEmby(NotifyBase): | ||||||
|         try: |         try: | ||||||
|             results = loads(r.content) |             results = loads(r.content) | ||||||
| 
 | 
 | ||||||
|         except ValueError: |         except (AttributeError, TypeError, ValueError): | ||||||
|             # A string like '' would cause this; basicallly the content |             # ValueError = r.content is Unparsable | ||||||
|             # that was provided was not a JSON string. We can stop here |             # TypeError = r.content is None | ||||||
|  |             # AttributeError = r is None | ||||||
|  | 
 | ||||||
|  |             # This is a problem; abort | ||||||
|             return False |             return False | ||||||
| 
 | 
 | ||||||
|         # Acquire our Access Token |         # Acquire our Access Token | ||||||
|  | @ -400,10 +403,12 @@ class NotifyEmby(NotifyBase): | ||||||
|         try: |         try: | ||||||
|             results = loads(r.content) |             results = loads(r.content) | ||||||
| 
 | 
 | ||||||
|         except ValueError: |         except (AttributeError, TypeError, ValueError): | ||||||
|             # A string like '' would cause this; basicallly the content |             # ValueError = r.content is Unparsable | ||||||
|             # that was provided was not a JSON string. There is nothing |             # TypeError = r.content is None | ||||||
|             # more we can do at this point |             # AttributeError = r is None | ||||||
|  | 
 | ||||||
|  |             # We need to abort at this point | ||||||
|             return sessions |             return sessions | ||||||
| 
 | 
 | ||||||
|         for entry in results: |         for entry in results: | ||||||
|  |  | ||||||
|  | @ -333,9 +333,10 @@ class NotifyGitter(NotifyBase): | ||||||
|             try: |             try: | ||||||
|                 content = loads(r.content) |                 content = loads(r.content) | ||||||
| 
 | 
 | ||||||
|             except (TypeError, ValueError): |             except (AttributeError, TypeError, ValueError): | ||||||
|                 # ValueError = r.content is Unparsable |                 # ValueError = r.content is Unparsable | ||||||
|                 # TypeError = r.content is None |                 # TypeError = r.content is None | ||||||
|  |                 # AttributeError = r is None | ||||||
|                 content = {} |                 content = {} | ||||||
| 
 | 
 | ||||||
|             try: |             try: | ||||||
|  |  | ||||||
|  | @ -921,8 +921,11 @@ class NotifyMatrix(NotifyBase): | ||||||
|                     # Return; we're done |                     # Return; we're done | ||||||
|                     return (False, response) |                     return (False, response) | ||||||
| 
 | 
 | ||||||
|             except ValueError: |             except (AttributeError, TypeError, ValueError): | ||||||
|                 # This gets thrown if we can't parse our JSON Response |                 # This gets thrown if we can't parse our JSON Response | ||||||
|  |                 #  - ValueError = r.content is Unparsable | ||||||
|  |                 #  - TypeError = r.content is None | ||||||
|  |                 #  - AttributeError = r is None | ||||||
|                 self.logger.warning('Invalid response from Matrix server.') |                 self.logger.warning('Invalid response from Matrix server.') | ||||||
|                 self.logger.debug( |                 self.logger.debug( | ||||||
|                     'Response Details:\r\n{}'.format(r.content)) |                     'Response Details:\r\n{}'.format(r.content)) | ||||||
|  |  | ||||||
|  | @ -307,10 +307,13 @@ class NotifyPushBullet(NotifyBase): | ||||||
|             try: |             try: | ||||||
|                 response = loads(r.content) |                 response = loads(r.content) | ||||||
| 
 | 
 | ||||||
|             except (TypeError, AttributeError, ValueError): |             except (AttributeError, TypeError, ValueError): | ||||||
|                 # AttributeError means r.content was None |                 # ValueError = r.content is Unparsable | ||||||
|  |                 # TypeError = r.content is None | ||||||
|  |                 # AttributeError = r is None | ||||||
|  | 
 | ||||||
|  |                 # Fall back to the existing unparsed value | ||||||
|                 response = r.content |                 response = r.content | ||||||
|                 pass |  | ||||||
| 
 | 
 | ||||||
|             if r.status_code not in ( |             if r.status_code not in ( | ||||||
|                     requests.codes.ok, requests.codes.no_content): |                     requests.codes.ok, requests.codes.no_content): | ||||||
|  |  | ||||||
|  | @ -564,9 +564,19 @@ class NotifyRocketChat(NotifyBase): | ||||||
|                 self.headers['X-User-Id'] = response.get( |                 self.headers['X-User-Id'] = response.get( | ||||||
|                     'data', {'userId': None}).get('userId') |                     'data', {'userId': None}).get('userId') | ||||||
| 
 | 
 | ||||||
|  |         except (AttributeError, TypeError, ValueError): | ||||||
|  |             # Our response was not the JSON type we had expected it to be | ||||||
|  |             # - ValueError = r.content is Unparsable | ||||||
|  |             # - TypeError = r.content is None | ||||||
|  |             # - AttributeError = r is None | ||||||
|  |             self.logger.warning( | ||||||
|  |                 'A commuication error occured authenticating {} on ' | ||||||
|  |                 'Rocket.Chat.'.format(self.user)) | ||||||
|  |             return False | ||||||
|  | 
 | ||||||
|         except requests.RequestException as e: |         except requests.RequestException as e: | ||||||
|             self.logger.warning( |             self.logger.warning( | ||||||
|                 'A Connection error occured authenticating {} on ' |                 'A connection error occured authenticating {} on ' | ||||||
|                 'Rocket.Chat.'.format(self.user)) |                 'Rocket.Chat.'.format(self.user)) | ||||||
|             self.logger.debug('Socket Exception: %s' % str(e)) |             self.logger.debug('Socket Exception: %s' % str(e)) | ||||||
|             return False |             return False | ||||||
|  |  | ||||||
|  | @ -518,8 +518,10 @@ class NotifySlack(NotifyBase): | ||||||
|             try: |             try: | ||||||
|                 response = loads(r.content) |                 response = loads(r.content) | ||||||
| 
 | 
 | ||||||
|             except (AttributeError, ValueError): |             except (AttributeError, TypeError, ValueError): | ||||||
|                 # AttributeError means r.content was None |                 # ValueError = r.content is Unparsable | ||||||
|  |                 # TypeError = r.content is None | ||||||
|  |                 # AttributeError = r is None | ||||||
|                 pass |                 pass | ||||||
| 
 | 
 | ||||||
|             if not (response and response.get('ok', True)): |             if not (response and response.get('ok', True)): | ||||||
|  |  | ||||||
|  | @ -378,6 +378,9 @@ class NotifyTelegram(NotifyBase): | ||||||
|             'Telegram User Detection POST URL: %s (cert_verify=%r)' % ( |             'Telegram User Detection POST URL: %s (cert_verify=%r)' % ( | ||||||
|                 url, self.verify_certificate)) |                 url, self.verify_certificate)) | ||||||
| 
 | 
 | ||||||
|  |         # Track our response object | ||||||
|  |         response = None | ||||||
|  | 
 | ||||||
|         try: |         try: | ||||||
|             r = requests.post( |             r = requests.post( | ||||||
|                 url, |                 url, | ||||||
|  | @ -392,9 +395,12 @@ class NotifyTelegram(NotifyBase): | ||||||
| 
 | 
 | ||||||
|                 try: |                 try: | ||||||
|                     # Try to get the error message if we can: |                     # Try to get the error message if we can: | ||||||
|                     error_msg = loads(r.content)['description'] |                     error_msg = loads(r.content).get('description', 'unknown') | ||||||
| 
 | 
 | ||||||
|                 except Exception: |                 except (AttributeError, TypeError, ValueError): | ||||||
|  |                     # ValueError = r.content is Unparsable | ||||||
|  |                     # TypeError = r.content is None | ||||||
|  |                     # AttributeError = r is None | ||||||
|                     error_msg = None |                     error_msg = None | ||||||
| 
 | 
 | ||||||
|                 if error_msg: |                 if error_msg: | ||||||
|  | @ -414,6 +420,18 @@ class NotifyTelegram(NotifyBase): | ||||||
| 
 | 
 | ||||||
|                 return 0 |                 return 0 | ||||||
| 
 | 
 | ||||||
|  |             # Load our response and attempt to fetch our userid | ||||||
|  |             response = loads(r.content) | ||||||
|  | 
 | ||||||
|  |         except (AttributeError, TypeError, ValueError): | ||||||
|  |             # Our response was not the JSON type we had expected it to be | ||||||
|  |             # - ValueError = r.content is Unparsable | ||||||
|  |             # - TypeError = r.content is None | ||||||
|  |             # - AttributeError = r is None | ||||||
|  |             self.logger.warning( | ||||||
|  |                 'A communication error occured detecting the Telegram User.') | ||||||
|  |             return 0 | ||||||
|  | 
 | ||||||
|         except requests.RequestException as e: |         except requests.RequestException as e: | ||||||
|             self.logger.warning( |             self.logger.warning( | ||||||
|                 'A connection error occured detecting the Telegram User.') |                 'A connection error occured detecting the Telegram User.') | ||||||
|  | @ -442,8 +460,6 @@ class NotifyTelegram(NotifyBase): | ||||||
|         #      "text":"/start", |         #      "text":"/start", | ||||||
|         #      "entities":[{"offset":0,"length":6,"type":"bot_command"}]}}] |         #      "entities":[{"offset":0,"length":6,"type":"bot_command"}]}}] | ||||||
| 
 | 
 | ||||||
|         # Load our response and attempt to fetch our userid |  | ||||||
|         response = loads(r.content) |  | ||||||
|         if 'ok' in response and response['ok'] is True \ |         if 'ok' in response and response['ok'] is True \ | ||||||
|                 and 'result' in response and len(response['result']): |                 and 'result' in response and len(response['result']): | ||||||
|             entry = response['result'][0] |             entry = response['result'][0] | ||||||
|  | @ -584,9 +600,13 @@ class NotifyTelegram(NotifyBase): | ||||||
| 
 | 
 | ||||||
|                     try: |                     try: | ||||||
|                         # Try to get the error message if we can: |                         # Try to get the error message if we can: | ||||||
|                         error_msg = loads(r.content)['description'] |                         error_msg = loads(r.content).get( | ||||||
|  |                             'description', 'unknown') | ||||||
| 
 | 
 | ||||||
|                     except Exception: |                     except (AttributeError, TypeError, ValueError): | ||||||
|  |                         # ValueError = r.content is Unparsable | ||||||
|  |                         # TypeError = r.content is None | ||||||
|  |                         # AttributeError = r is None | ||||||
|                         error_msg = None |                         error_msg = None | ||||||
| 
 | 
 | ||||||
|                     self.logger.warning( |                     self.logger.warning( | ||||||
|  |  | ||||||
|  | @ -321,11 +321,13 @@ class NotifyTwilio(NotifyBase): | ||||||
|                         status_code = json_response.get('code', status_code) |                         status_code = json_response.get('code', status_code) | ||||||
|                         status_str = json_response.get('message', status_str) |                         status_str = json_response.get('message', status_str) | ||||||
| 
 | 
 | ||||||
|                     except (AttributeError, ValueError): |                     except (AttributeError, TypeError, ValueError): | ||||||
|                         # could not parse JSON response... just use the status |                         # ValueError = r.content is Unparsable | ||||||
|                         # we already have. |                         # TypeError = r.content is None | ||||||
|  |                         # AttributeError = r is None | ||||||
| 
 | 
 | ||||||
|                         # AttributeError means r.content was None |                         # We could not parse JSON response. | ||||||
|  |                         # We will just use the status we already have. | ||||||
|                         pass |                         pass | ||||||
| 
 | 
 | ||||||
|                     self.logger.warning( |                     self.logger.warning( | ||||||
|  |  | ||||||
|  | @ -534,9 +534,10 @@ class NotifyTwitter(NotifyBase): | ||||||
|             try: |             try: | ||||||
|                 content = loads(r.content) |                 content = loads(r.content) | ||||||
| 
 | 
 | ||||||
|             except (TypeError, ValueError): |             except (AttributeError, TypeError, ValueError): | ||||||
|                 # ValueError = r.content is Unparsable |                 # ValueError = r.content is Unparsable | ||||||
|                 # TypeError = r.content is None |                 # TypeError = r.content is None | ||||||
|  |                 # AttributeError = r is None | ||||||
|                 content = {} |                 content = {} | ||||||
| 
 | 
 | ||||||
|             try: |             try: | ||||||
|  |  | ||||||
|  | @ -5061,7 +5061,14 @@ def test_notify_rocketchat_plugin(mock_post, mock_get): | ||||||
|     # |     # | ||||||
|     assert obj.logout() is True |     assert obj.logout() is True | ||||||
| 
 | 
 | ||||||
|  |     # Invalid JSON during Login | ||||||
|  |     mock_post.return_value.content = '{' | ||||||
|  |     mock_get.return_value.content = '}' | ||||||
|  |     assert obj.login() is False | ||||||
|  | 
 | ||||||
|     # Prepare Mock to fail |     # Prepare Mock to fail | ||||||
|  |     mock_post.return_value.content = '' | ||||||
|  |     mock_get.return_value.content = '' | ||||||
|     mock_post.return_value.status_code = requests.codes.internal_server_error |     mock_post.return_value.status_code = requests.codes.internal_server_error | ||||||
|     mock_get.return_value.status_code = requests.codes.internal_server_error |     mock_get.return_value.status_code = requests.codes.internal_server_error | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -70,6 +70,22 @@ def test_notify_telegram_plugin(mock_post, mock_get, tmpdir): | ||||||
|     with pytest.raises(TypeError): |     with pytest.raises(TypeError): | ||||||
|         plugins.NotifyTelegram(bot_token=None, targets=chat_ids) |         plugins.NotifyTelegram(bot_token=None, targets=chat_ids) | ||||||
| 
 | 
 | ||||||
|  |     # Invalid JSON while trying to detect bot owner | ||||||
|  |     mock_get.return_value.content = '{' | ||||||
|  |     mock_post.return_value.content = '}' | ||||||
|  |     with pytest.raises(TypeError): | ||||||
|  |         plugins.NotifyTelegram(bot_token=bot_token, targets=None) | ||||||
|  | 
 | ||||||
|  |     # Invalid JSON while trying to detect bot owner + 400 error | ||||||
|  |     mock_get.return_value.status_code = requests.codes.internal_server_error | ||||||
|  |     mock_post.return_value.status_code = requests.codes.internal_server_error | ||||||
|  |     with pytest.raises(TypeError): | ||||||
|  |         plugins.NotifyTelegram(bot_token=bot_token, targets=None) | ||||||
|  | 
 | ||||||
|  |     # Return status back to how they were | ||||||
|  |     mock_post.return_value.status_code = requests.codes.ok | ||||||
|  |     mock_get.return_value.status_code = requests.codes.ok | ||||||
|  | 
 | ||||||
|     # Exception should be thrown about the fact an invalid bot token was |     # Exception should be thrown about the fact an invalid bot token was | ||||||
|     # specifed |     # specifed | ||||||
|     with pytest.raises(TypeError): |     with pytest.raises(TypeError): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Chris Caron
						Chris Caron