[ui] Add count of messages to commit configdocs CLI
The list of validations returned from commit configdocs can be long and sometimes lose sight of errors. This change adds a summary count of the severity level of messages returned at the bottom of the output so as to clue the user into what might be closer to the top. Change-Id: I2a8921b22977b8ceab0afb1341b195c12dc49c67
This commit is contained in:
parent
89840303b1
commit
a51e30f0a2
@ -61,12 +61,21 @@ def cli_format_status_handler(response, is_error=False):
|
|||||||
resp_j = response.json()
|
resp_j = response.json()
|
||||||
resp = formatted.format(resp_j.get('message', 'Not specified'),
|
resp = formatted.format(resp_j.get('message', 'Not specified'),
|
||||||
resp_j.get('reason', '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'):
|
if resp_j.get('details'):
|
||||||
mlist = resp_j['details'].get('messageList', [])
|
mlist = resp_j['details'].get('messageList', [])
|
||||||
for message in sorted(mlist,
|
# Decorate messages with level number and sortkey
|
||||||
key=lambda m: _lvl_key(
|
for message in mlist:
|
||||||
m.get('level'),
|
message['lnum'], message['sortkey'] = _lvl_key(
|
||||||
m.get('error', False))):
|
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':
|
if message.get('kind') == 'ValidationMessage':
|
||||||
resp = resp + _format_validation_message(message)
|
resp = resp + _format_validation_message(message)
|
||||||
else:
|
else:
|
||||||
@ -76,6 +85,11 @@ def cli_format_status_handler(response, is_error=False):
|
|||||||
_INDENT,
|
_INDENT,
|
||||||
message['source']
|
message['source']
|
||||||
)
|
)
|
||||||
|
# Append a count summary
|
||||||
|
resp = resp + ("\n\n#### Errors: {},"
|
||||||
|
" Warnings: {},"
|
||||||
|
" Infos: {},"
|
||||||
|
" Other: {} ####".format(*lvl_counts))
|
||||||
return resp
|
return resp
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
@ -92,14 +106,18 @@ _LEVEL_KEYS = {
|
|||||||
1: ['warn', 'warning'],
|
1: ['warn', 'warning'],
|
||||||
2: ['info', 'debug'],
|
2: ['info', 'debug'],
|
||||||
}
|
}
|
||||||
_SENTINEL_LEVEL = "999"
|
_SENTINEL_SORT_KEY = "3none"
|
||||||
|
_SENTINEL_LEVEL = 3
|
||||||
|
|
||||||
|
|
||||||
def _lvl_key(level_name, error):
|
def _lvl_key(level_name, error):
|
||||||
"""Generate a level key value
|
"""Generate a level key value and sort key
|
||||||
|
|
||||||
Returns a value to support sort order based on lvls dict.
|
Returns a value to support sort order based on lvls dict.
|
||||||
The result is that like-level items are sorted together.
|
The result is that like-level items are sorted together.
|
||||||
|
The level number provides sameness for levels that are named differently
|
||||||
|
but are the same level for our purposes. The sort key retains the original
|
||||||
|
provided level so that like named items still sort together.
|
||||||
"""
|
"""
|
||||||
if level_name is None:
|
if level_name is None:
|
||||||
if (error):
|
if (error):
|
||||||
@ -111,8 +129,8 @@ def _lvl_key(level_name, error):
|
|||||||
|
|
||||||
for key, val_list in _LEVEL_KEYS.items():
|
for key, val_list in _LEVEL_KEYS.items():
|
||||||
if level_name in val_list:
|
if level_name in val_list:
|
||||||
return '{}{}'.format(key, level_name)
|
return key, '{}{}'.format(key, level_name)
|
||||||
return _SENTINEL_LEVEL
|
return _SENTINEL_LEVEL, _SENTINEL_SORT_KEY
|
||||||
|
|
||||||
|
|
||||||
def _format_validation_message(message):
|
def _format_validation_message(message):
|
||||||
|
@ -60,7 +60,6 @@ def test_cli_format_error_handler_no_messages():
|
|||||||
resp = MagicMock()
|
resp = MagicMock()
|
||||||
resp.json = MagicMock(return_value=json.loads(resp_val))
|
resp.json = MagicMock(return_value=json.loads(resp_val))
|
||||||
output = format_utils.cli_format_error_handler(resp)
|
output = format_utils.cli_format_error_handler(resp)
|
||||||
print(output)
|
|
||||||
assert "Error: Unauthenticated" in output
|
assert "Error: Unauthenticated" in output
|
||||||
assert "Reason: Credentials are not established" in output
|
assert "Reason: Credentials are not established" in output
|
||||||
|
|
||||||
@ -208,7 +207,74 @@ Reason: Validation
|
|||||||
Document: schema/schema/v1 - someyaml
|
Document: schema/schema/v1 - someyaml
|
||||||
Source: format-o-matic
|
Source: format-o-matic
|
||||||
- Info: Basic info
|
- Info: Basic info
|
||||||
Source: Armadadock"""
|
Source: Armadadock
|
||||||
|
|
||||||
|
#### Errors: 4, Warnings: 0, Infos: 2, Other: 0 ####"""
|
||||||
|
resp = MagicMock()
|
||||||
|
resp.json = MagicMock(return_value=json.loads(resp_val))
|
||||||
|
output = format_utils.cli_format_status_handler(resp, is_error=True)
|
||||||
|
assert output == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_format_status_handler_messages_empty():
|
||||||
|
"""Tests the generic handler for shipyard status response if passed
|
||||||
|
a response with no message in the detail to ensure the empty case works.
|
||||||
|
"""
|
||||||
|
resp_val = """
|
||||||
|
{
|
||||||
|
"apiVersion": "v1.0",
|
||||||
|
"status": "Failure",
|
||||||
|
"metadata": {},
|
||||||
|
"message": "Component Validation Failed",
|
||||||
|
"code": 400,
|
||||||
|
"details": {
|
||||||
|
"errorCount": 0,
|
||||||
|
"messageList": []
|
||||||
|
},
|
||||||
|
"kind": "Status",
|
||||||
|
"reason": "Validation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
expected = """Error: Component Validation Failed
|
||||||
|
Reason: Validation
|
||||||
|
|
||||||
|
#### Errors: 0, Warnings: 0, Infos: 0, Other: 0 ####"""
|
||||||
|
resp = MagicMock()
|
||||||
|
resp.json = MagicMock(return_value=json.loads(resp_val))
|
||||||
|
output = format_utils.cli_format_status_handler(resp, is_error=True)
|
||||||
|
assert output == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_format_status_handler_messages_invalid_levels():
|
||||||
|
"""Tests the generic handler for shipyard status response if passed
|
||||||
|
a response with no message in the detail to ensure the empty case works.
|
||||||
|
"""
|
||||||
|
resp_val = """
|
||||||
|
{
|
||||||
|
"apiVersion": "v1.0",
|
||||||
|
"status": "Failure",
|
||||||
|
"metadata": {},
|
||||||
|
"message": "Component Validation Failed",
|
||||||
|
"code": 400,
|
||||||
|
"details": {
|
||||||
|
"errorCount": 0,
|
||||||
|
"messageList": [
|
||||||
|
{ "message": "It is broken",
|
||||||
|
"level": "Broken",
|
||||||
|
"kind": "ValidationMessage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"kind": "Status",
|
||||||
|
"reason": "Validation"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
expected = """Error: Component Validation Failed
|
||||||
|
Reason: Validation
|
||||||
|
- Broken: None
|
||||||
|
Message: It is broken
|
||||||
|
|
||||||
|
#### Errors: 0, Warnings: 0, Infos: 0, Other: 1 ####"""
|
||||||
resp = MagicMock()
|
resp = MagicMock()
|
||||||
resp.json = MagicMock(return_value=json.loads(resp_val))
|
resp.json = MagicMock(return_value=json.loads(resp_val))
|
||||||
output = format_utils.cli_format_status_handler(resp, is_error=True)
|
output = format_utils.cli_format_status_handler(resp, is_error=True)
|
||||||
|
@ -39,7 +39,6 @@ def test_add_role():
|
|||||||
def test_add_roles():
|
def test_add_roles():
|
||||||
'''test add_roles'''
|
'''test add_roles'''
|
||||||
ctx = ShipyardRequestContext()
|
ctx = ShipyardRequestContext()
|
||||||
print(ctx.roles)
|
|
||||||
test_roles = ['Waiter', 'Host', 'Chef']
|
test_roles = ['Waiter', 'Host', 'Chef']
|
||||||
ctx.add_roles(test_roles)
|
ctx.add_roles(test_roles)
|
||||||
assert ['Chef', 'Host', 'Waiter', 'anyone'] == sorted(ctx.roles)
|
assert ['Chef', 'Host', 'Waiter', 'anyone'] == sorted(ctx.roles)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user