Skip to content

Commit 472c326

Browse files
committed
feat: include action_id in formatted output of response containing ActionLink
1 parent b32c429 commit 472c326

4 files changed

Lines changed: 70 additions & 2 deletions

File tree

src/binarylane/console/printers/formatter.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
def format_response(response: Any, show_header: bool, fields: Optional[List[str]] = None) -> List[List[str]]:
1515
"""Convert structured response object into a 'table' (where the length of each inner list is the same)"""
1616

17+
action_id: Optional[int] = _get_action_id(response)
1718
response = _extract_primary(response)
1819

1920
if isinstance(response, list):
@@ -40,6 +41,10 @@ def object_to_list(row: Dict[str, Any], columns: List[str]) -> List[Any]:
4041
data = [["name", "value"]] if show_header else []
4142
data += [_flatten(item, True) for item in response.to_dict().items()]
4243

44+
# If response contained an action ID, prepend it to the formatted data
45+
if action_id:
46+
data.insert(1 if show_header else 0, ["action_id", str(action_id)])
47+
4348
return data
4449

4550

@@ -127,3 +132,18 @@ def _flatten_dict(item: Dict[str, Any], single_object: bool) -> str:
127132

128133
# Generic handler
129134
return "<object>" if not single_object else "\n".join([f"{key}: {value}" for key, value in item.items()])
135+
136+
137+
def _get_action_id(response: Any) -> Optional[int]:
138+
"""Return response.links.actions[0].id or None"""
139+
140+
if not (links := getattr(response, "links", None)):
141+
return None
142+
143+
if not (actions := getattr(links, "actions", None)) or not isinstance(actions, list):
144+
return None
145+
146+
if not (action_id := getattr(actions[0], "id", None)) or not isinstance(action_id, int):
147+
return None
148+
149+
return action_id

src/binarylane/console/runners/actionlink.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ def response(self, status_code: int, received: Any) -> None:
1616
super().response(status_code, received)
1717
return
1818

19-
action_id = links.actions[0].id
20-
super().response(status_code, action_id)
19+
# Show action progress on stdout
20+
if not self._async:
21+
action_id = links.actions[0].id
22+
super().response(status_code, action_id)
2123

2224
# Print the 'other' object (e.g. server) from the response
2325
self._printer.print(received)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, Dict, TypeVar
4+
5+
import attr
6+
7+
# We have to use the real ActionsLinks, because handler checks for it by type
8+
from binarylane.models.actions_links import ActionsLinks
9+
from tests.models.server import Server
10+
11+
12+
@attr.s(auto_attribs=True)
13+
class CreateServerResponse:
14+
server: Server
15+
links: ActionsLinks
16+
additional_properties: Dict[str, Any] = attr.ib(init=False, factory=dict)
17+
18+
def to_dict(self) -> Dict[str, Any]:
19+
server = self.server.to_dict()
20+
21+
links = self.links.to_dict()
22+
23+
field_dict: Dict[str, Any] = {}
24+
field_dict.update(self.additional_properties)
25+
field_dict.update(
26+
{
27+
"server": server,
28+
"links": links,
29+
}
30+
)
31+
32+
return field_dict

tests/printers/test_formatter.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from typing import Any, Dict, List
44

5+
from binarylane.models.action_link import ActionLink
6+
from binarylane.models.actions_links import ActionsLinks
7+
from tests.models.create_server_response import CreateServerResponse
58
from tests.models.network import Network
69
from tests.models.network_type import NetworkType
710
from tests.models.servers_response import ServersResponse
@@ -97,3 +100,14 @@ def test_format_networks_v4_and_v6(servers_response: ServersResponse) -> None:
97100
def test_format_int() -> None:
98101
assert formatter.format_response(12345, True) == [[formatter.DEFAULT_HEADING], ["12345"]]
99102
assert formatter.format_response(12345, False) == [["12345"]]
103+
104+
105+
def test_format_action_link(servers_response: ServersResponse) -> None:
106+
action_link = ActionLink(12345, "create", "https://api.example.com/v2/actions/12345")
107+
response = CreateServerResponse(servers_response.servers[0], ActionsLinks([action_link]))
108+
assert formatter.format_response(response, True)[:4] == [
109+
["name", "value"],
110+
["action_id", "12345"],
111+
["id", "1"],
112+
["name", "test"],
113+
]

0 commit comments

Comments
 (0)