diff --git a/aiourlshortener/exceptions.py b/aiourlshortener/exceptions.py index 86c0b88..e5b933b 100644 --- a/aiourlshortener/exceptions.py +++ b/aiourlshortener/exceptions.py @@ -2,6 +2,10 @@ class UnknownAioUrlShortenerError(Exception): pass +class FetchError(Exception): + """A error occurred while fetching a remote resource""" + + class ShorteningError(Exception): pass diff --git a/aiourlshortener/shorteners/base.py b/aiourlshortener/shorteners/base.py index 99736b3..5e35a5a 100644 --- a/aiourlshortener/shorteners/base.py +++ b/aiourlshortener/shorteners/base.py @@ -1,7 +1,10 @@ +import asyncio from abc import abstractmethod import aiohttp from asyncio import coroutine +from ..exceptions import FetchError + class BaseShortener(object): """ @@ -16,14 +19,23 @@ def __init__(self, **kwargs): @coroutine def _get(self, url: str, params=None, headers=None): - with aiohttp.Timeout(self.kwargs['timeout']): - response = yield from self._session.get(url, params=params, headers=headers) - return response + response = yield from self._fetch('GET', url, params=params, headers=headers) + return response @coroutine def _post(self, url: str, data=None, params=None, headers=None): - with aiohttp.Timeout(self.kwargs['timeout']): - response = yield from self._session.post(url, data=data, params=params, headers=headers) + response = yield from self._fetch('POST', url, data=data, params=params, headers=headers) + return response + + @coroutine + def _fetch(self, method: str, url: str, data=None, params=None, headers=None): + try: + with aiohttp.Timeout(self.kwargs['timeout']): + response = yield from self._session.request(method, url, data=data, params=params, headers=headers) + response.raise_for_status() + except (aiohttp.ClientError, asyncio.TimeoutError): + raise FetchError() + else: return response @abstractmethod diff --git a/aiourlshortener/shorteners/bitly.py b/aiourlshortener/shorteners/bitly.py index 6181c3a..ec33303 100644 --- a/aiourlshortener/shorteners/bitly.py +++ b/aiourlshortener/shorteners/bitly.py @@ -4,8 +4,10 @@ """ from asyncio import coroutine +import aiohttp + from .base import BaseShortener -from ..exceptions import ShorteningError, ExpandingError +from ..exceptions import ShorteningError, ExpandingError, FetchError class Bitly(BaseShortener): @@ -23,17 +25,29 @@ def __init__(self, **kwargs): @coroutine def short(self, url: str) -> str: params = {'access_token': self.access_token, 'longUrl': url, 'format': 'json'} - response = yield from self._get(self._short_url, params=params) - response = yield from response.json() + response = {} + try: + response = yield from self._get(self._short_url, params=params) + response = yield from response.json() + except (aiohttp.ClientError, FetchError) as err: + raise ShorteningError('There was an error shortening the url "{}": {}'.format(url, repr(err))) + if 'data' in response and isinstance(response['data'], dict) and 'url' in response['data']: return response['data']['url'] - raise ShorteningError('There was an error shortening this url: {}'.format(response)) + + raise ShorteningError('Detected an api change for Bitly') @coroutine def expand(self, url: str) -> str: params = {'access_token': self.access_token, 'link': url, 'format': 'json'} - response = yield from self._get(self._expand_url, params=params) - response = yield from response.json() + response = {} + try: + response = yield from self._get(self._expand_url, params=params) + response = yield from response.json() + except (aiohttp.ClientError, FetchError) as err: + raise ExpandingError('There was an error expanding the url "{}": {}'.format(url, repr(err))) + if 'data' in response and isinstance(response['data'], dict) and 'original_url' in response['data']: return response['data']['original_url'] - raise ExpandingError('There was an error expanding this url: {}'.format(response)) + + raise ExpandingError('Detected an api change for Bitly') diff --git a/aiourlshortener/shorteners/google.py b/aiourlshortener/shorteners/google.py index 3a4131b..abfbda2 100644 --- a/aiourlshortener/shorteners/google.py +++ b/aiourlshortener/shorteners/google.py @@ -5,8 +5,10 @@ import json from asyncio import coroutine +import aiohttp + from .base import BaseShortener -from ..exceptions import ShorteningError, ExpandingError +from ..exceptions import ShorteningError, ExpandingError, FetchError class Google(BaseShortener): @@ -24,17 +26,29 @@ def __init__(self, **kwargs): def short(self, url: str) -> str: data = {'longUrl': url} params = {'key': self.api_key} - response = yield from self._post(self.api_url, data=json.dumps(data), params=params, headers=self._headers) - response = yield from response.json() + response = {} + try: + response = yield from self._post(self.api_url, data=json.dumps(data), params=params, headers=self._headers) + response = yield from response.json() + except (aiohttp.ClientError, FetchError) as err: + raise ShorteningError('There was an error shortening the url "{}": {}'.format(url,repr(err))) + if 'id' in response: return response['id'] - raise ShorteningError('There was an error shortening this url: {}'.format(response)) + + raise ShorteningError('Detected an api change for Google') @coroutine def expand(self, url: str) -> str: params = {'key': self.api_key, 'shortUrl': url} - response = yield from self._get(self.api_url, params=params, headers=self._headers) - response = yield from response.json() + response = {} + try: + response = yield from self._get(self.api_url, params=params, headers=self._headers) + response = yield from response.json() + except (aiohttp.ClientError, FetchError) as err: + raise ExpandingError('There was an error expanding the url "{}": {}'.format(url, repr(err))) + if 'longUrl' in response: return response['longUrl'] - raise ExpandingError('There was an error expanding this url: {}'.format(response)) + + raise ExpandingError('Detected an api change for Google')