diff --git a/changelog.d/20240502_124425_sirosen_flows_error_refinement.md b/changelog.d/20240502_124425_sirosen_flows_error_refinement.md new file mode 100644 index 000000000..333d5c16f --- /dev/null +++ b/changelog.d/20240502_124425_sirosen_flows_error_refinement.md @@ -0,0 +1,3 @@ +### Enhancements + +* Improve the reporting of errors found by `globus flows validate` diff --git a/src/globus_cli/exception_handling/hooks.py b/src/globus_cli/exception_handling/hooks.py index a96938cf8..49bd78d07 100644 --- a/src/globus_cli/exception_handling/hooks.py +++ b/src/globus_cli/exception_handling/hooks.py @@ -358,14 +358,35 @@ def flows_error_hook(exception: globus_sdk.FlowsAPIError) -> None: details = textwrap.fill(details, width=80) detail_fields = [PrintableErrorField("detail", details, multiline=True)] # if it's a list of objects, wrap them into a multiline detail field - else: - detail_fields = [ - PrintableErrorField( - "detail", - "\n".join(_pretty_json(detail, compact=True) for detail in details), - multiline=True, - ) - ] + elif isinstance(details, list): + num_errors = len(details) + if all((isinstance(d, dict) and "loc" in d and "msg" in d) for d in details): + detail_strings = [ + ( + ((data["type"] + " ") if "type" in data else "") + + f"{_jsonpath_from_pydantic_loc(data['loc'])}: {data['msg']}" + ) + for data in details + ] + if num_errors == 1: + detail_fields = [PrintableErrorField("detail", detail_strings[0])] + else: + detail_fields = [ + PrintableErrorField("detail", f"{num_errors} errors"), + PrintableErrorField( + "errors", + "\n".join(detail_strings), + multiline=True, + ), + ] + else: + detail_fields = [ + PrintableErrorField( + "detail", + "\n".join(_pretty_json(detail, compact=True) for detail in details), + multiline=True, + ) + ] write_error_info( "Flows API Error", diff --git a/tests/functional/exception_handling/test_flows_hooks.py b/tests/functional/exception_handling/test_flows_hooks.py index da78d173b..945f3a497 100644 --- a/tests/functional/exception_handling/test_flows_hooks.py +++ b/tests/functional/exception_handling/test_flows_hooks.py @@ -96,6 +96,7 @@ def test_flows_generic_hook_on_detail_array(run_line): assert "This flow contains errors in 1 wait state." in result.stderr assert "detail:" in result.stderr assert ( + "InvalidAccessPath $.definition.States.y.SecondsPath: " "Improper access to data in this wait state with expression: $.foo[0]" in result.stderr )