Skip to content

Commit a8d0d52

Browse files
authored
new: Display region_prices in linode types output (#533)
1 parent 14dfd2a commit a8d0d52

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

linodecli/overrides.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
large changes to the OpenAPI spec.
55
"""
66

7+
from rich.align import Align
8+
from rich.console import Console
9+
from rich.table import Table
10+
711
from linodecli.output import OutputMode
812

913
OUTPUT_OVERRIDES = {}
@@ -37,3 +41,92 @@ def handle_domains_zone_file(operation, output_handler, json_data) -> bool:
3741
"""
3842
print("\n".join(json_data["zone_file"]))
3943
return False
44+
45+
46+
@output_override("linodes", "types", OutputMode.table)
47+
def handle_types_region_prices_list(
48+
operation, output_handler, json_data
49+
) -> bool:
50+
"""
51+
Override the output of 'linode-cli linodes types' to display regional pricing.
52+
"""
53+
return linode_types_with_region_prices(operation, output_handler, json_data)
54+
55+
56+
def linode_types_with_region_prices(
57+
operation, output_handler, json_data
58+
) -> bool:
59+
# pylint: disable=unused-argument
60+
"""
61+
Parse and reformat linode types output with region prices.
62+
"""
63+
if len(json_data["data"]) < 1:
64+
return True
65+
66+
output = Table()
67+
68+
# To ensure the order of the headers and make sure we have region_prices as the last column
69+
headers = sorted(
70+
json_data["data"][0].keys() - ["addons", "price", "region_prices"],
71+
key=len,
72+
)
73+
headers += ["price.hourly", "price.monthly", "region_prices"]
74+
75+
for header in headers:
76+
output.add_column(header, justify="center")
77+
78+
for linode in json_data["data"]:
79+
row = []
80+
for h in headers:
81+
if h == "region_prices":
82+
sub_table = format_region_prices(linode[h])
83+
row.append(sub_table)
84+
85+
elif h in ("price.hourly", "price.monthly"):
86+
price = format_prices(h, linode)
87+
row.append(Align(price, align="left"))
88+
89+
else:
90+
row.append(Align(str(linode[h]), align="left"))
91+
92+
output.add_row(*row)
93+
94+
console = Console()
95+
console.print(output)
96+
97+
print(
98+
"See our [Pricing Page](https://www.linode.com/pricing/) for Region-specific pricing, "
99+
+ "which applies after migration is complete."
100+
)
101+
102+
return False
103+
104+
105+
def format_prices(prices, data: dict[str, any]) -> any:
106+
"""
107+
Format nested price entry.
108+
"""
109+
price_headers = prices.split(".")
110+
111+
return str(data[price_headers[0]][price_headers[1]])
112+
113+
114+
def format_region_prices(data: dict[str, any]) -> any:
115+
"""
116+
Format nested region price entry into a sub-table.
117+
"""
118+
subheaders = ["id", "hourly", "monthly"]
119+
120+
sub_table = Table()
121+
122+
for header in subheaders:
123+
sub_table.add_column(header, justify="center")
124+
125+
for region_price in data:
126+
region_price_row = (
127+
Align(str(region_price[header]), align="left")
128+
for header in subheaders
129+
)
130+
sub_table.add_row(*region_price_row)
131+
132+
return sub_table

tests/unit/test_overrides.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,59 @@ def patch_func(*a):
5656
)
5757

5858
assert stdout_buf.getvalue() != "line 1\nline 2\n"
59+
60+
def test_types_region_prices_list(
61+
self, mock_cli, list_operation_for_overrides_test
62+
):
63+
response_json = {
64+
"data": [
65+
{
66+
"addons": {
67+
"backups": {
68+
"price": {"hourly": 0.008, "monthly": 5},
69+
"region_prices": [
70+
{
71+
"hourly": 0.0096,
72+
"id": "us-east",
73+
"monthly": 6,
74+
}
75+
],
76+
}
77+
},
78+
"class": "standard",
79+
"disk": 81920,
80+
"gpus": 0,
81+
"id": "g6-standard-2",
82+
"label": "Linode 4GB",
83+
"memory": 4096,
84+
"network_out": 1000,
85+
"price": {"hourly": 0.03, "monthly": 20},
86+
"region_prices": [
87+
{"hourly": 0.036, "id": "us-east", "monthly": 24}
88+
],
89+
"successor": None,
90+
"transfer": 4000,
91+
"vcpus": 2,
92+
}
93+
],
94+
"page": 1,
95+
"pages": 1,
96+
"results": 1,
97+
}
98+
99+
override_signature = ("linodes", "types", OutputMode.table)
100+
101+
list_operation_for_overrides_test.command = "linodes"
102+
list_operation_for_overrides_test.action = "types"
103+
mock_cli.output_handler.mode = OutputMode.table
104+
105+
stdout_buf = io.StringIO()
106+
107+
with contextlib.redirect_stdout(stdout_buf):
108+
list_operation_for_overrides_test.process_response_json(
109+
response_json, mock_cli.output_handler
110+
)
111+
112+
rows = stdout_buf.getvalue().split("\n")
113+
# assert that the overridden table has the new columns
114+
assert len(rows[1].split(rows[1][0])) == 15

0 commit comments

Comments
 (0)