From 30950202b323a49d21170941f5e46ab6077da5e9 Mon Sep 17 00:00:00 2001 From: Nishant Kumar Date: Mon, 10 Dec 2018 19:11:57 +0000 Subject: [PATCH] Raise specific errors during create configdocs Currently shipyard raises same error for 2 scenarios- - Collection empty or resulted in no revision (Error code - 400) - Buffer is either not empty or the collection already exists in buffer (Error code - 409) This PS enables shipyard to raise specific errors for the above given scenarios which would equip consumers of Shipyard API client to handle the exception gracefully. Change-Id: I10860ca60f4fde4088cbb146283a2db305418320 --- .../shipyard_client/api_client/base_client.py | 6 ++ .../api_client/client_error.py | 8 ++ .../shipyard_client/cli/action.py | 6 ++ .../shipyard_client/cli/format_utils.py | 94 +++++++++++-------- 4 files changed, 77 insertions(+), 37 deletions(-) diff --git a/src/bin/shipyard_client/shipyard_client/api_client/base_client.py b/src/bin/shipyard_client/shipyard_client/api_client/base_client.py index 68570959..6f08b3fb 100644 --- a/src/bin/shipyard_client/shipyard_client/api_client/base_client.py +++ b/src/bin/shipyard_client/shipyard_client/api_client/base_client.py @@ -23,6 +23,8 @@ import requests from shipyard_client.api_client.client_error import ClientError from shipyard_client.api_client.client_error import UnauthenticatedClientError from shipyard_client.api_client.client_error import UnauthorizedClientError +from shipyard_client.api_client.client_error import ShipyardBufferError +from shipyard_client.api_client.client_error import InvalidCollectionError class BaseClient(metaclass=abc.ABCMeta): @@ -99,6 +101,10 @@ class BaseClient(metaclass=abc.ABCMeta): raise UnauthenticatedClientError() if response.status_code == 403: raise UnauthorizedClientError() + if response.status_code == 400: + raise InvalidCollectionError(response.text) + if response.status_code == 409: + raise ShipyardBufferError(response.text) return response except requests.exceptions.RequestException as e: self.error(str(e)) diff --git a/src/bin/shipyard_client/shipyard_client/api_client/client_error.py b/src/bin/shipyard_client/shipyard_client/api_client/client_error.py index f6af7423..19707d65 100644 --- a/src/bin/shipyard_client/shipyard_client/api_client/client_error.py +++ b/src/bin/shipyard_client/shipyard_client/api_client/client_error.py @@ -23,3 +23,11 @@ class UnauthorizedClientError(ClientError): class UnauthenticatedClientError(ClientError): pass + + +class ShipyardBufferError(ClientError): + pass + + +class InvalidCollectionError(ClientError): + pass diff --git a/src/bin/shipyard_client/shipyard_client/cli/action.py b/src/bin/shipyard_client/shipyard_client/cli/action.py index b5884bde..bc96b8a8 100644 --- a/src/bin/shipyard_client/shipyard_client/cli/action.py +++ b/src/bin/shipyard_client/shipyard_client/cli/action.py @@ -19,6 +19,8 @@ import logging from shipyard_client.api_client.client_error import ClientError from shipyard_client.api_client.client_error import UnauthenticatedClientError from shipyard_client.api_client.client_error import UnauthorizedClientError +from shipyard_client.api_client.client_error import ShipyardBufferError +from shipyard_client.api_client.client_error import InvalidCollectionError from shipyard_client.api_client.shipyard_api_client import ShipyardClient from shipyard_client.api_client.shipyardclient_context import \ ShipyardClientContext @@ -134,6 +136,10 @@ class CliAction(AbstractCliAction): "Check credential values") except UnauthorizedClientError: self.resp_txt = "Error: Unauthorized to perform this action." + except ShipyardBufferError as ex: + self.resp_txt = format_utils.cli_format_exception_handler(ex) + except InvalidCollectionError as ex: + self.resp_txt = format_utils.cli_format_exception_handler(ex) except ClientError as ex: self.resp_txt = "Error: Client Error: {}".format(str(ex)) except Exception as ex: diff --git a/src/bin/shipyard_client/shipyard_client/cli/format_utils.py b/src/bin/shipyard_client/shipyard_client/cli/format_utils.py index 5ab1c9c6..0568238f 100644 --- a/src/bin/shipyard_client/shipyard_client/cli/format_utils.py +++ b/src/bin/shipyard_client/shipyard_client/cli/format_utils.py @@ -47,11 +47,7 @@ def cli_format_status_handler(response, is_error=False): standard error format :is_error: toggles the use of status or error verbiage :returns: a generically formatted error response formulated from the - client_repsonse. The response will be in the format: - - [Error|Status]: {{message}} - Reason: {{Reason}} - Additional: {{details message list messages}} + client_repsonse. ... """ formatted = "Error: {}\nReason: {}" if is_error \ @@ -59,44 +55,68 @@ def cli_format_status_handler(response, is_error=False): try: if response.text: resp_j = response.json() - resp = formatted.format(resp_j.get('message', 'Not specified'), - resp_j.get('reason', 'Not specified')) - # lvl_counts must have a matching number of values as the - # _LEVEL_KEYS below + 1 for sentinel. - lvl_counts = [0, 0, 0, 0] - if resp_j.get('details'): - mlist = resp_j['details'].get('messageList', []) - # Decorate messages with level number and sortkey - for message in mlist: - message['lnum'], message['sortkey'] = _lvl_key( - message.get('level'), - message.get('error', False) - ) - # Sort and formulate the messages - for message in sorted(mlist, key=lambda m: m['sortkey']): - lnum = message['lnum'] - lvl_counts[lnum] = lvl_counts[lnum] + 1 - if message.get('kind') == 'ValidationMessage': - resp = resp + _format_validation_message(message) - else: - resp = resp + _format_basic_message(message) - if message.get('source'): - resp = resp + "\n{}Source: {}".format( - _INDENT, - message['source'] - ) - # Append a count summary - resp = resp + ("\n\n#### Errors: {}," - " Warnings: {}," - " Infos: {}," - " Other: {} ####".format(*lvl_counts)) - return resp + return cli_format_response(formatted, resp_j) else: return '' except ValueError: return "Error: Unable to decode response. Value: {}".format( response.text) +def cli_format_exception_handler(exc_msg): + """ Formatter for custom error raised by Shipyard """ + try: + formatted = "Error: {}\nReason: {}" + # Convert exception message to dict from string + exc_dict = json.loads(str(exc_msg)) + return cli_format_response(formatted, exc_dict) + except Exception: + return "Error: Unable to decode response. Value: {}".format( + exc_msg) + +def cli_format_response(formatted, response): + """ Handler for Shipyard status and error responses + + :param formatted: structure to be used for response + :param response: client response to be formatted + + The response will be in the format: + [Error|Status]: {{message}} + Reason: {{Reason}} + Additional: {{details message list messages}} + """ + resp = formatted.format(response.get('message', 'Not specified'), + response.get('reason', 'Not specified')) + # lvl_counts must have a matching number of values as the + # _LEVEL_KEYS below + 1 for sentinel. + lvl_counts = [0, 0, 0, 0] + if response.get('details'): + mlist = response['details'].get('messageList', []) + # Decorate messages with level number and sortkey + for message in mlist: + message['lnum'], message['sortkey'] = _lvl_key( + message.get('level'), + message.get('error', False) + ) + # Sort and formulate the messages + for message in sorted(mlist, key=lambda m: m['sortkey']): + lnum = message['lnum'] + lvl_counts[lnum] = lvl_counts[lnum] + 1 + if message.get('kind') == 'ValidationMessage': + resp = resp + _format_validation_message(message) + else: + resp = resp + _format_basic_message(message) + if message.get('source'): + resp = resp + "\n{}Source: {}".format( + _INDENT, + message['source'] + ) + # Append a count summary + resp = resp + ("\n\n#### Errors: {}," + " Warnings: {}," + " Infos: {}," + " Other: {} ####".format(*lvl_counts)) + return resp + # Map of levels by severity. Extra values are included in this map but valid # values are defined here: