Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
10 changes: 5 additions & 5 deletions src/fabric_cli/core/fab_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def __init__(
error_code: Optional[str] = None,
data: Optional[Any] = None,
hidden_data: Optional[Any] = None,
show_key_value_pretty: bool = False,
show_key_value_list: bool = False,
):
"""Initialize a new FabricCLIOutput instance.

Expand All @@ -90,7 +90,7 @@ def __init__(
error_code: Optional error code. Only included when status is Failed.
data: The main output data to be displayed
hidden_data: Additional data shown only when --all flag or FAB_SHOW_HIDDEN is true
show_key_value_pretty: Whether to show output in key-value pretty format
show_key_value_list: Whether to show output in key-value list format

Note:
The data parameter is always converted to a list format internally.
Expand All @@ -102,7 +102,7 @@ def __init__(
self._subcommand = subcommand
self._output_format_type = output_format_type
self._show_headers = show_headers
self._show_key_value_pretty = show_key_value_pretty
self._show_key_value_list = show_key_value_list

self._result = OutputResult(
data=data,
Expand All @@ -128,8 +128,8 @@ def show_headers(self) -> bool:
return self._show_headers

@property
def show_key_value_pretty(self) -> bool:
return self._show_key_value_pretty
def show_key_value_list(self) -> bool:
return self._show_key_value_list

def to_json(self, indent: int = 4) -> str:
try:
Expand Down
4 changes: 4 additions & 0 deletions src/fabric_cli/errors/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

class CommonErrors:

@staticmethod
def invalid_entries_format() -> str:
return "Invalid entries format"

@staticmethod
def invalid_jmespath_query() -> str:
return f"Invalid jmespath query (https://jmespath.org)"
Expand Down
43 changes: 28 additions & 15 deletions src/fabric_cli/utils/fab_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import builtins
import html
import json
import re
import sys
import unicodedata
from argparse import Namespace
Expand Down Expand Up @@ -95,7 +96,7 @@ def print_output_format(
data: Optional[Any] = None,
hidden_data: Optional[Any] = None,
show_headers: bool = False,
show_key_value_pretty: bool = False,
show_key_value_list: bool = False,
) -> None:
"""Create a FabricCLIOutput instance and print it depends on the format.

Expand All @@ -105,7 +106,7 @@ def print_output_format(
data: Optional data to include in output
hidden_data: Optional hidden data to include in output
show_headers: Whether to show headers in the output (default: False)
show_key_value_pretty: Whether to show output in key-value pretty format (default: False)
show_key_value_list: Whether to show output in key-value list format (default: False)

Returns:
FabricCLIOutput: Configured output instance ready for printing
Expand All @@ -122,7 +123,7 @@ def print_output_format(
data=data,
hidden_data=hidden_data,
show_headers=show_headers,
show_key_value_pretty=show_key_value_pretty,
show_key_value_list=show_key_value_list,
)

# Get format from output or config
Expand Down Expand Up @@ -357,8 +358,8 @@ def _print_output_format_result_text(output: FabricCLIOutput) -> None:
):
data_keys = output.result.get_data_keys() if output_result.data else []
print_entries_unix_style(output_result.data, data_keys, header=show_headers)
elif output.show_key_value_pretty:
_print_entries_key_value_pretty_style(output_result.data)
elif output.show_key_value_list:
_print_entries_key_value_list_style(output_result.data)
else:
_print_raw_data(output_result.data)

Expand Down Expand Up @@ -492,8 +493,8 @@ def _get_visual_length(string: str) -> int:
return length


def _print_entries_key_value_pretty_style(entries: Any) -> None:
"""Print entries in a key-value list format with pretty-formatted keys.
def _print_entries_key_value_list_style(entries: Any) -> None:
"""Print entries in a key-value list format with formatted keys.

Args:
entries: Dictionary or list of dictionaries to print
Expand All @@ -510,29 +511,41 @@ def _print_entries_key_value_pretty_style(entries: Any) -> None:
_entries = entries
else:
raise FabricCLIError(
ErrorMessages.Labels.invalid_entries_format(),
ErrorMessages.Common.invalid_entries_format(),
fab_constant.ERROR_INVALID_ENTRIES_FORMAT,
)

for entry in _entries:
for key, value in entry.items():
pretty_key = _format_key_to_pretty_name(key)
pretty_key = _format_key_to_convert_to_title_case(key)
print_grey(f"{pretty_key}: {value}", to_stderr=False)
if len(_entries) > 1:
print_grey("", to_stderr=False) # Empty line between entries


def _format_key_to_pretty_name(key: str) -> str:
"""Convert a snake_case or camelCase key to a Title Case pretty name.
def _format_key_to_convert_to_title_case(key: str) -> str:
"""Convert a snake_case or camelCase key to a Title Case name.

Args:
key: The key to format (e.g. 'logged_in' or 'accountName')
key: The key to format (e.g. 'user_id' or 'accountName')

Returns:
str: Formatted pretty name (e.g. 'Logged In' or 'Account Name')
str: Formatted to title case name (e.g. 'User Id' or 'Account Name')
"""
# Replace underscores and camelCase with spaces
pretty = key.replace('_', ' ')
pretty = ''.join(' ' + char if char.isupper() else char for char in pretty).strip()
# pretty = ''.join(' ' + char if char.isupper() else char for char in pretty).strip()
pretty = re.sub(r'(?<!^)(?<! )(?=[A-Z])', ' ', pretty)
# Title case the result
return pretty.title()
pretty = pretty.title()

special_cases = {
"Id": "ID",
"Powerbi": "PowerBI",
}

# Replace special cases
for key, value in special_cases.items():
pretty = pretty.replace(key.title(), value)

return pretty
18 changes: 9 additions & 9 deletions tests/test_core/test_fab_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,16 @@ def test_fabric_cli_output_error_handling_success():
assert json_output["result"]["error_code"] == "UnexpectedError"


def test_fabric_cli_output_show_key_value_pretty_success():
"""Test show_key_value_pretty property is handled correctly."""
# Test with show_key_value_pretty True
output = FabricCLIOutput(data={"test": "data"}, show_key_value_pretty=True)
assert output.show_key_value_pretty is True
def test_fabric_cli_output_show_key_value_list_success():
"""Test show_key_value_list property is handled correctly."""
# Test with show_key_value_list True
output = FabricCLIOutput(data={"test": "data"}, show_key_value_list=True)
assert output.show_key_value_list is True

# Test with show_key_value_pretty False (default)
# Test with show_key_value_list False (default)
output = FabricCLIOutput(data={"test": "data"})
assert output.show_key_value_pretty is False
assert output.show_key_value_list is False

# Test with explicit False
output = FabricCLIOutput(data={"test": "data"}, show_key_value_pretty=False)
assert output.show_key_value_pretty is False
output = FabricCLIOutput(data={"test": "data"}, show_key_value_list=False)
assert output.show_key_value_list is False
53 changes: 23 additions & 30 deletions tests/test_utils/test_fab_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,10 +560,10 @@ def test_print_output_format_with_force_output_success(
)


def test_print_output_format_with_show_key_value_pretty_success(
def test_print_output_format_with_show_key_value_list_success(
mock_questionary_print, mock_fab_set_state_config
):
"""Test print_output_format with show_key_value_pretty=True calls print_entries_key_value_style."""
"""Test print_output_format with show_key_value_list=True calls print_entries_key_value_style."""

# Setup text output format
mock_fab_set_state_config(constant.FAB_OUTPUT_FORMAT, "text")
Expand All @@ -578,7 +578,7 @@ def test_print_output_format_with_show_key_value_pretty_success(
ui.print_output_format(
args,
data=test_data,
show_key_value_pretty=True
show_key_value_list=True
)

assert mock_questionary_print.call_count >= 1
Expand All @@ -594,10 +594,10 @@ def test_print_output_format_with_show_key_value_pretty_success(
mock_questionary_print.reset_mock()


def test_print_output_format_with_show_key_value_pretty_false_success(
def test_print_output_format_with_show_key_value_list_false_success(
mock_questionary_print, mock_fab_set_state_config
):
"""Test print_output_format with show_key_value_pretty=False uses default JSON formatting."""
"""Test print_output_format with show_key_value_list=False uses default JSON formatting."""

# Setup text output format
mock_fab_set_state_config(constant.FAB_OUTPUT_FORMAT, "text")
Expand All @@ -609,7 +609,7 @@ def test_print_output_format_with_show_key_value_pretty_false_success(
ui.print_output_format(
args,
data=test_data,
show_key_value_pretty=False # Explicitly set to False
show_key_value_list=False # Explicitly set to False
)

assert mock_questionary_print.call_count == 1
Expand All @@ -622,10 +622,10 @@ def test_print_output_format_with_show_key_value_pretty_false_success(
mock_questionary_print.reset_mock()


def test_print_output_format_with_show_key_value_pretty_json_format_success(
def test_print_output_format_with_show_key_value_list_json_format_success(
mock_questionary_print, mock_fab_set_state_config
):
"""Test that show_key_value_pretty parameter works correctly with JSON output format."""
"""Test that show_key_value_list parameter works correctly with JSON output format."""

# Setup JSON output format
mock_fab_set_state_config(constant.FAB_OUTPUT_FORMAT, "json")
Expand All @@ -637,10 +637,10 @@ def test_print_output_format_with_show_key_value_pretty_json_format_success(
ui.print_output_format(
args,
data=test_data,
show_key_value_pretty=True # This should be ignored in JSON format
show_key_value_list=True # This should be ignored in JSON format
)

# Verify that JSON output is produced regardless of show_key_value_pretty
# Verify that JSON output is produced regardless of show_key_value_list
assert mock_questionary_print.call_count == 1
output = json.loads(mock_questionary_print.mock_calls[0].args[0])

Expand Down Expand Up @@ -687,7 +687,7 @@ def test_print_entries_key_value_style_success(capsys):

# Test with single dictionary entry
entry = {"logged_in": "true", "account_name": "johndoe@example.com"}
ui.print_entries_key_value_pretty_style(entry)
ui._print_entries_key_value_list_style(entry)

captured = capsys.readouterr()
# print_grey outputs to stderr with to_stderr=False, so check stdout
Expand All @@ -700,7 +700,7 @@ def test_print_entries_key_value_style_success(capsys):
{"user_name": "john", "status": "active"},
{"user_name": "jane", "status": "inactive"}
]
ui.print_entries_key_value_pretty_style(entries)
ui._print_entries_key_value_list_style(entries)

captured = capsys.readouterr()
output = captured.out
Expand All @@ -710,7 +710,7 @@ def test_print_entries_key_value_style_success(capsys):
assert "Status: inactive" in output

# Test with empty list
ui.print_entries_key_value_pretty_style([])
ui._print_entries_key_value_list_style([])
captured = capsys.readouterr()
# Should not output anything for empty list
assert captured.err == ""
Expand All @@ -722,36 +722,29 @@ def test_print_entries_key_value_style_invalid_input():

# Test with invalid input type (string)
with pytest.raises(FabricCLIError) as ex:
ui.print_entries_key_value_pretty_style("invalid_input")
ui._print_entries_key_value_list_style("invalid_input")

assert ex.value.status_code == fab_constant.ERROR_INVALID_ENTRIES_FORMAT

# Test with invalid input type (integer)
with pytest.raises(FabricCLIError) as ex:
ui.print_entries_key_value_pretty_style(123)
ui._print_entries_key_value_list_style(123)

assert ex.value.status_code == fab_constant.ERROR_INVALID_ENTRIES_FORMAT


def test_format_key_to_pretty_name():
"""Test the key formatting function used in key-value style output."""

def test_format_key_to_title_case_success():
# Test snake_case conversion
assert ui._format_key_to_pretty_name("logged_in") == "Logged In"
assert ui._format_key_to_pretty_name("account_name") == "Account Name"
assert ui._format_key_to_pretty_name("user_id") == "User Id"

assert ui._format_key_to_convert_to_title_case("account_name") == "Account Name"
# Test camelCase conversion
assert ui._format_key_to_pretty_name("accountName") == "Account Name"
assert ui._format_key_to_pretty_name("userName") == "User Name"
assert ui._format_key_to_pretty_name("isActive") == "Is Active"

assert ui._format_key_to_convert_to_title_case("accountName") == "Account Name"
# Test single word
assert ui._format_key_to_pretty_name("status") == "Status"
assert ui._format_key_to_pretty_name("name") == "Name"

assert ui._format_key_to_convert_to_title_case("status") == "Status"
# Test mixed case
assert ui._format_key_to_pretty_name("user_Name") == "User Name"
assert ui._format_key_to_convert_to_title_case("user_Name") == "User Name"
# Test special cases from the function
assert ui._format_key_to_convert_to_title_case("user_id") == "User ID"
assert ui._format_key_to_convert_to_title_case("powerbi_settings") == "PowerBI Settings"


def test_print_version_seccess():
Expand Down