Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pretty connection error messages #127

Merged
merged 3 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion sap/adt/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ def __init__(self, host, client, user, password, port=None, ssl=True, verify=Tru
port = '80'
self._ssl_verify = verify

self._host = host
self._port = port
self._ssl = ssl
self._adt_uri = 'sap/bc/adt'
self._base_url = f'{protocol}://{host}:{port}/{self._adt_uri}'
self._query_args = f'sap-client={client}&saml2=disabled'
Expand Down Expand Up @@ -173,7 +176,8 @@ def _retrieve(self, session, method, url, params=None, headers=None, body=None):
except requests.exceptions.ConnectTimeout as ex:
raise TimedOutRequestError(req, self._timeout) from ex
except requests.exceptions.ConnectionError as ex:
raise ADTConnectionError(ex.args[-1]) from ex
msg = str(ex)
raise ADTConnectionError(self._host, self._port, self._ssl, msg) from ex

mod_log().debug('Response %s %s:\n++++\n%s\n++++', method, url, res.text)

Expand Down
11 changes: 9 additions & 2 deletions sap/adt/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,16 @@ def __str__(self):
class ADTConnectionError(SAPCliError):
"""Wrapper for ADT connection errors"""

def __init__(self, message):
def __init__(self, host, port, ssl, message):
super().__init__()
self.message = message
msg = f'[HOST:"{host}", PORT:"{port}", SSL:"{ssl}"] Error: '
if 'Errno -5' in message:
msg += 'Name resolution error. Check the HOST configuration.'
elif 'Errno 111' in message:
msg += 'Cannot connect to the system. Check the HOST and PORT configuration.'
else:
msg += message
self.message = msg

def __str__(self):
return f'ADT Connection error: {self.message}'
Expand Down
6 changes: 5 additions & 1 deletion sap/rest/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ def __init__(self, icf_path, login_path, host, client, user, password, port=None

self._ssl_verify = verify

self._host = host
self._port = port
self._ssl = ssl
self._base_url = f'{protocol}://{host}:{port}/{icf_path}'
self._query_args = f'sap-client={client}&saml2=disabled'

Expand Down Expand Up @@ -127,7 +130,8 @@ def _retrieve(self, session, method, url, params=None, headers=None, body=None):
except requests.exceptions.ConnectTimeout as ex:
raise TimedOutRequestError(req, self._timeout) from ex
except requests.exceptions.ConnectionError as ex:
raise GCTSConnectionError(ex.args[-1]) from ex
msg = str(ex)
raise GCTSConnectionError(self._host, self._port, self._ssl, msg) from ex

mod_log().debug('Response %s %s:\n++++\n%s\n++++', method, url, res.text)

Expand Down
12 changes: 10 additions & 2 deletions sap/rest/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,16 @@ def __str__(self):
class GCTSConnectionError(SAPCliError):
"""Exception for connection errors"""

def __init__(self, message):
self.message = message
def __init__(self, host, port, ssl, message):
super().__init__()
msg = f'[HOST:"{host}", PORT:"{port}", SSL:"{ssl}"] Error: '
if 'Errno -5' in message:
msg += 'Name resolution error. Check the HOST configuration.'
elif 'Errno 111' in message:
msg += 'Cannot connect to the system. Check the HOST and PORT configuration.'
else:
msg += message
self.message = msg

def __str__(self):
return f'GCTS connection error: {self.message}'
4 changes: 2 additions & 2 deletions sap/rfc/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ def connect(**kwargs):
try:
return SAPRFC_MODULE.Connection(**kwargs)
except SAPRFC_MODULE._exception.LogonError as exc:
raise RFCLoginError(exc) from exc
raise RFCLoginError(kwargs['ashost'], kwargs['user'], exc) from exc
except SAPRFC_MODULE._exception.CommunicationError as exc:
raise RFCCommunicationError(exc) from exc
raise RFCCommunicationError(kwargs['ashost'], kwargs['user'], exc) from exc


def try_pyrfc_exception_type():
Expand Down
12 changes: 6 additions & 6 deletions sap/rfc/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
class RFCConnectionError(SAPCliError):
"""Wrapper for RFC connection error"""

def __init__(self, message):
self.message = message
def __init__(self, host, user, message):
self.message = f'[HOST:"{host}", USER:"{user}"] Error: {message}'

def __str__(self):
return f'RFC connection error: {self.message}'
Expand All @@ -15,15 +15,15 @@ def __str__(self):
class RFCLoginError(RFCConnectionError):
"""Wrapper for RFC Login error"""

def __init__(self, exception):
super().__init__(exception.message)
def __init__(self, host, user, exception):
super().__init__(host, user, exception.message)


class RFCCommunicationError(RFCConnectionError):
"""Wrapper for RFC Communication error"""

def __init__(self, exception):
def __init__(self, host, user, exception):
msg = exception.message.split('\n')
msg = next(filter(lambda line: line.startswith('ERROR'), msg))
msg = ' '.join(msg.split()[1:])
super().__init__(msg)
super().__init__(host, user, msg)
30 changes: 28 additions & 2 deletions test/unit/test_sap_adt_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,41 @@ def test_execute_session_refetch_csfr_headers(self, fake_retrieve, fake_accepts)
resp = self.connection.execute('GET', 'test', headers={'awesome': 'fabulous'})
self.assertEqual(resp.text, 'success')

@patch('sap.adt.core.requests.Request')
def test_protocol_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('Remote end closed connection without response')

with self.assertRaises(sap.adt.errors.ADTConnectionError) as cm:
self.connection._retrieve(session, 'method', 'url')

self.assertEqual(str(cm.exception),
f'ADT Connection error: [HOST:"{self.connection._host}", PORT:"{self.connection._port}", '
f'SSL:"{self.connection._ssl}"] Error: Remote end closed connection without response')

@patch('sap.adt.core.requests.Request')
def test_dns_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('[Errno -5] Dummy name resolution error.')

with self.assertRaises(sap.adt.errors.ADTConnectionError) as cm:
self.connection._retrieve(session, 'method', 'url')

self.assertEqual(str(cm.exception),
f'ADT Connection error: [HOST:"{self.connection._host}", PORT:"{self.connection._port}", '
f'SSL:"{self.connection._ssl}"] Error: Name resolution error. Check the HOST configuration.')

@patch('sap.adt.core.requests.Request')
def test_connection_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('Wrong creds')
session.send.side_effect = ConnectionError('[Errno 111] Dummy connection error.')

with self.assertRaises(sap.adt.errors.ADTConnectionError) as cm:
self.connection._retrieve(session, 'method', 'url')

self.assertEqual(str(cm.exception), 'ADT Connection error: Wrong creds')
self.assertEqual(str(cm.exception),
f'ADT Connection error: [HOST:"{self.connection._host}", PORT:"{self.connection._port}", '
f'SSL:"{self.connection._ssl}"] Error: Cannot connect to the system. Check the HOST and PORT configuration.')


if __name__ == '__main__':
Expand Down
56 changes: 45 additions & 11 deletions test/unit/test_sap_rest_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ def stub_retrieve(response, session, method, url, params=None, headers=None, bod

class TestConnectionExecute(unittest.TestCase):

def setUp(self):
icf_path = '/foo'
login_path = '/bar'
host = 'books.fr'
client = '69'
user = 'Arsan'
password = 'Emmanuelle'

self.conn = Connection(icf_path, login_path, host, client, user, password)

@patch('sap.rest.connection.Connection._retrieve')
def test_unauthorized_error(self, fake_retrieve):
icf_path = '/foo'
Expand All @@ -46,22 +56,46 @@ def test_unauthorized_error(self, fake_retrieve):
self.assertEqual(str(caught.exception), f'Authorization for the user "{user}" has failed: {method} {url}')

@patch('sap.rest.connection.requests.Request')
def test_connection_error(self, _):
def test_protocol_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('Wrong creds')
session.send.side_effect = ConnectionError('Remote end closed connection without response')

icf_path = '/foo'
login_path = '/bar'
host = 'books.fr'
client = '69'
user = 'Arsan'
password = 'Emmanuelle'
method = 'GET'
url = '/all'

conn = Connection(icf_path, login_path, host, client, user, password)
with self.assertRaises(GCTSConnectionError) as cm:
self.conn._retrieve(session, method, url)

self.assertEqual(str(cm.exception),
f'GCTS connection error: [HOST:"{self.conn._host}", PORT:"443", '
'SSL:"True"] Error: Remote end closed connection without response')

@patch('sap.rest.connection.requests.Request')
def test_dns_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('[Errno -5] Dummy name resolution error')

method = 'GET'
url = '/all'

with self.assertRaises(GCTSConnectionError) as cm:
self.conn._retrieve(session, method, url)

self.assertEqual(str(cm.exception),
f'GCTS connection error: [HOST:"{self.conn._host}", PORT:"443", '
'SSL:"True"] Error: Name resolution error. Check the HOST configuration.')

@patch('sap.rest.connection.requests.Request')
def test_connection_error(self, _):
session = Mock()
session.send.side_effect = ConnectionError('[Errno 111] Dummy connection error')

method = 'GET'
url = '/all'

with self.assertRaises(GCTSConnectionError) as cm:
conn._retrieve(session, method, url)
self.conn._retrieve(session, method, url)

self.assertEqual(str(cm.exception), 'GCTS connection error: Wrong creds')
self.assertEqual(str(cm.exception),
f'GCTS connection error: [HOST:"{self.conn._host}", PORT:"443", '
'SSL:"True"] Error: Cannot connect to the system. Check the HOST and PORT configuration.')
8 changes: 4 additions & 4 deletions test/unit/test_sap_rfc_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ def __init__(self, msg):
patch('sap.rfc.core.SAPRFC_MODULE', new=fake_saprfc_module).start()

with self.assertRaises(RFCLoginError) as cm:
sap.rfc.core.connect()
sap.rfc.core.connect(ashost='host', user='user')
print('the exception is not raised')

self.assertEqual(str(cm.exception), 'RFC connection error: Bad login')
self.assertEqual(str(cm.exception), 'RFC connection error: [HOST:"host", USER:"user"] Error: Bad login')

def test_pyrfc_communication_error(self):
class FakeLogonError(Exception):
Expand All @@ -52,6 +52,6 @@ def __init__(self, msg):
patch('sap.rfc.core.SAPRFC_MODULE', new=fake_saprfc_module).start()

with self.assertRaises(RFCCommunicationError) as cm:
sap.rfc.core.connect()
sap.rfc.core.connect(ashost='host', user='user')

self.assertEqual(str(cm.exception), 'RFC connection error: Communication error')
self.assertEqual(str(cm.exception), 'RFC connection error: [HOST:"host", USER:"user"] Error: Communication error')
Loading