Skip to content

Commit b023fa5

Browse files
committed
Resync-password and rotate-certificate output formatting Fix
1 parent e60945d commit b023fa5

11 files changed

Lines changed: 719 additions & 1 deletion

File tree

src/managednetworkfabric/HISTORY.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Release History
44
===============
55

6+
9.1.1
7+
++++++
8+
* Fix CLI output formatting for `fabric` and `device` `resync-password` and `fabric` `rotate-certificate` commands to prevent duplicated nested error details in both successful and partial-success responses.
9+
610
9.1.0
711
++++++
812
* Enables the following previously removed command/command groups:

src/managednetworkfabric/azext_managednetworkfabric/commands.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ def load_command_table(self, _): # pylint: disable=unused-argument
1919

2020
self.command_table["networkfabric device run-ro"] = RunReadCommand(loader=self)
2121

22+
from .custom.device_resync_password import (
23+
ResyncPasswordCommand as DeviceResyncPasswordCommand,
24+
)
25+
26+
self.command_table["networkfabric device resync-password"] = (
27+
DeviceResyncPasswordCommand(loader=self)
28+
)
29+
2230
with self.command_group("networkfabric internetgateway"):
2331

2432
from .operations.internetgateway._show import ShowCommand
@@ -32,3 +40,21 @@ def load_command_table(self, _): # pylint: disable=unused-argument
3240
self.command_table["networkfabric internetgateway list"] = ListCommand(
3341
loader=self
3442
)
43+
44+
with self.command_group("networkfabric fabric"):
45+
46+
from .custom.fabric_resync_password import (
47+
ResyncPasswordCommand as FabricResyncPasswordCommand,
48+
)
49+
50+
self.command_table["networkfabric fabric resync-password"] = (
51+
FabricResyncPasswordCommand(loader=self)
52+
)
53+
54+
from .custom.fabric_rotate_certificate import (
55+
RotateCertificateCommand as FabricRotateCertificateCommand,
56+
)
57+
58+
self.command_table["networkfabric fabric rotate-certificate"] = (
59+
FabricRotateCertificateCommand(loader=self)
60+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
#
5+
# --------------------------------------------------------------------------------------------
6+
# pylint: disable=protected-access,duplicate-code
7+
8+
"""
9+
This code inherits the auto-generated code for device resync-password command, and adds
10+
custom error formatting.
11+
"""
12+
13+
from azure.core.exceptions import HttpResponseError
14+
15+
from azext_managednetworkfabric.aaz.latest.networkfabric.device import (
16+
ResyncPassword as _ResyncPasswordCommand,
17+
)
18+
from azext_managednetworkfabric.operations.error_format import ErrorFormat
19+
20+
21+
class ResyncPasswordCommand(_ResyncPasswordCommand):
22+
"""Custom class for networkfabric device resync-password"""
23+
24+
def _handler(self, command_args):
25+
poller = super()._handler(command_args)
26+
if poller is None:
27+
return None
28+
try:
29+
return poller.result()
30+
except HttpResponseError as e:
31+
ErrorFormat.handle_lro_error(e)
32+
33+
def _output(self, *args, **kwargs):
34+
return ErrorFormat._output(self, *args, **kwargs)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
#
5+
# --------------------------------------------------------------------------------------------
6+
# pylint: disable=protected-access,duplicate-code
7+
8+
"""
9+
This code inherits the auto-generated code for fabric resync-password command, and adds
10+
custom error formatting.
11+
"""
12+
13+
from azure.core.exceptions import HttpResponseError
14+
15+
from azext_managednetworkfabric.aaz.latest.networkfabric.fabric import (
16+
ResyncPassword as _ResyncPasswordCommand,
17+
)
18+
from azext_managednetworkfabric.operations.error_format import ErrorFormat
19+
20+
21+
class ResyncPasswordCommand(_ResyncPasswordCommand):
22+
"""Custom class for networkfabric fabric resync-password"""
23+
24+
def _handler(self, command_args):
25+
poller = super()._handler(command_args)
26+
if poller is None:
27+
return None
28+
try:
29+
return poller.result()
30+
except HttpResponseError as e:
31+
ErrorFormat.handle_lro_error(e)
32+
33+
def _output(self, *args, **kwargs):
34+
return ErrorFormat._output(self, *args, **kwargs)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
#
5+
# --------------------------------------------------------------------------------------------
6+
# pylint: disable=protected-access,duplicate-code
7+
8+
"""
9+
This code inherits the auto-generated code for fabric rotate-certificate command, and adds
10+
custom error formatting.
11+
"""
12+
13+
from azure.core.exceptions import HttpResponseError
14+
15+
from azext_managednetworkfabric.aaz.latest.networkfabric.fabric import (
16+
RotateCertificate as _RotateCertificateCommand,
17+
)
18+
from azext_managednetworkfabric.operations.error_format import ErrorFormat
19+
20+
21+
class RotateCertificateCommand(_RotateCertificateCommand):
22+
"""Custom class for networkfabric fabric rotate-certificate"""
23+
24+
def _handler(self, command_args):
25+
poller = super()._handler(command_args)
26+
if poller is None:
27+
return None
28+
try:
29+
return poller.result()
30+
except HttpResponseError as e:
31+
ErrorFormat.handle_lro_error(e)
32+
33+
def _output(self, *args, **kwargs):
34+
return ErrorFormat._output(self, *args, **kwargs)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
#
5+
# Custom code that is added in addition to auto-generated by aaz-dev code.
6+
# --------------------------------------------------------------------------------------------
7+
# pylint: disable=too-many-lines,no-member,inconsistent-return-statements
8+
# pylint: disable=too-many-statements,too-few-public-methods
9+
10+
"""
11+
Helper class for formatting error responses with nested details.
12+
13+
The Azure SDK ODataV4Format.message_details() concatenates error detail entries
14+
without newline separators between them, causing all details to run together on
15+
single lines. This helper intercepts the HttpResponseError raised by the LRO
16+
poller and rebuilds the error message with proper formatting.
17+
"""
18+
19+
from knack.util import CLIError
20+
21+
22+
class ErrorFormat:
23+
"""Helper class for formatting error responses with nested details"""
24+
25+
@staticmethod
26+
def _output(parent_cmd, *args, **kwargs): # pylint: disable=unused-argument
27+
"""Custom response transform that prevents nested error details from being duplicated"""
28+
result = parent_cmd.deserialize_output(
29+
parent_cmd.ctx.vars.instance, client_flatten=True
30+
)
31+
32+
if result and isinstance(result, dict) and "error" in result:
33+
error = result.get("error")
34+
if error and isinstance(error, dict):
35+
if "details" in error and isinstance(error["details"], list):
36+
for detail in error["details"]:
37+
if isinstance(detail, dict) and "details" in detail:
38+
del detail["details"]
39+
40+
return result
41+
42+
@staticmethod
43+
def format_error_message(http_error):
44+
"""Build a properly formatted error message with newlines between detail entries.
45+
46+
Reproduces the ODataV4Format layout but fixes the missing newline between
47+
each detail block so they no longer run together on a single line.
48+
"""
49+
error = getattr(http_error, "error", None)
50+
if not error:
51+
return str(http_error)
52+
53+
code = getattr(error, "code", None) or "Unknown"
54+
message = getattr(error, "message", None) or str(http_error)
55+
56+
lines = [
57+
f"({code}) {message}",
58+
f"Code: {code}",
59+
f"Message: {message}",
60+
]
61+
62+
target = getattr(error, "target", None)
63+
if target:
64+
lines.append(f"Target: {target}")
65+
66+
details = getattr(error, "details", None)
67+
if details:
68+
lines.append("Exception Details:")
69+
for i, detail in enumerate(details):
70+
if i > 0:
71+
lines.append("")
72+
d_code = getattr(detail, "code", None) or "Unknown"
73+
d_msg = getattr(detail, "message", None) or ""
74+
lines.append(f"\t({d_code}) {d_msg}")
75+
lines.append(f"\tCode: {d_code}")
76+
lines.append(f"\tMessage: {d_msg}")
77+
d_target = getattr(detail, "target", None)
78+
if d_target:
79+
lines.append(f"\tTarget: {d_target}")
80+
81+
return "\n".join(lines)
82+
83+
@staticmethod
84+
def handle_lro_error(http_error):
85+
"""Raise a CLIError with a properly formatted error message."""
86+
raise CLIError(ErrorFormat.format_error_message(http_error)) from http_error
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)