mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
feat(api): add machine-readable error codes and align OpenAPI error schemas
This commit is contained in:
9 files changed
+398
-61
No files matched your search
@@ -710,7 +710,9 @@ class Controller:
|
||||
except KeyError:
|
||||
if compute_id == "vm":
|
||||
raise ControllerNotFoundError("Cannot use a node on the GNS3 VM server with the GNS3 VM not configured")
|
||||
raise ControllerNotFoundError(f"Compute ID {compute_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Compute ID {compute_id} doesn't exist", code="compute_not_found", details={"compute_id": compute_id}
|
||||
)
|
||||
|
||||
def has_compute(self, compute_id):
|
||||
"""
|
||||
@@ -748,7 +750,9 @@ class Controller:
|
||||
try:
|
||||
return self._projects[project_id]
|
||||
except KeyError:
|
||||
raise ControllerNotFoundError(f"Project ID {project_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Project ID {project_id} doesn't exist", code="project_not_found", details={"project_id": project_id}
|
||||
)
|
||||
|
||||
async def get_loaded_project(self, project_id):
|
||||
"""
|
||||
|
||||
@@ -15,11 +15,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ControllerError(Exception):
|
||||
def __init__(self, message: str):
|
||||
default_code = "conflict"
|
||||
|
||||
def __init__(self, message: str, code: str | None = None, details: dict[str, Any] | None = None):
|
||||
super().__init__()
|
||||
self._message = message
|
||||
self._code = code or self.default_code
|
||||
self._details = details
|
||||
|
||||
def __repr__(self):
|
||||
return self._message
|
||||
@@ -27,30 +33,33 @@ class ControllerError(Exception):
|
||||
def __str__(self):
|
||||
return self._message
|
||||
|
||||
@property
|
||||
def code(self) -> str:
|
||||
return self._code
|
||||
|
||||
@property
|
||||
def details(self) -> dict[str, Any] | None:
|
||||
return self._details
|
||||
|
||||
|
||||
class ControllerNotFoundError(ControllerError):
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
default_code = "not_found"
|
||||
|
||||
|
||||
class ControllerBadRequestError(ControllerError):
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
default_code = "bad_request"
|
||||
|
||||
|
||||
class ControllerUnauthorizedError(ControllerError):
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
default_code = "unauthorized"
|
||||
|
||||
|
||||
class ControllerForbiddenError(ControllerError):
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
default_code = "forbidden"
|
||||
|
||||
|
||||
class ControllerTimeoutError(ControllerError):
|
||||
def __init__(self, message: str):
|
||||
super().__init__(message)
|
||||
default_code = "timeout"
|
||||
|
||||
|
||||
class ComputeError(ControllerError):
|
||||
@@ -65,8 +74,10 @@ class ComputeConflictError(ComputeError):
|
||||
:param response: compute JSON response
|
||||
"""
|
||||
|
||||
default_code = "compute_conflict"
|
||||
|
||||
def __init__(self, url, response):
|
||||
super().__init__(response["message"])
|
||||
super().__init__(response["message"], code=response.get("code"), details=response.get("details"))
|
||||
self._url = url
|
||||
self._response = response
|
||||
|
||||
|
||||
@@ -274,9 +274,22 @@ class Link:
|
||||
|
||||
port = node.get_port(adapter_number, port_number)
|
||||
if port is None:
|
||||
raise ControllerNotFoundError(f"Port {adapter_number}/{port_number} for {node.name} not found")
|
||||
raise ControllerNotFoundError(
|
||||
f"Port {adapter_number}/{port_number} for {node.name} not found",
|
||||
code="port_not_found",
|
||||
details={"node_id": node.id, "adapter_number": adapter_number, "port_number": port_number},
|
||||
)
|
||||
if port.link is not None:
|
||||
raise ControllerError("Port is already used")
|
||||
raise ControllerError(
|
||||
"Port is already used",
|
||||
code="port_in_use",
|
||||
details={
|
||||
"node_id": node.id,
|
||||
"adapter_number": adapter_number,
|
||||
"port_number": port_number,
|
||||
"link_id": port.link.id,
|
||||
},
|
||||
)
|
||||
|
||||
self._link_type = port.link_type
|
||||
|
||||
|
||||
@@ -748,7 +748,9 @@ class Project:
|
||||
try:
|
||||
return self._nodes[node_id]
|
||||
except KeyError:
|
||||
raise ControllerNotFoundError(f"Node ID {node_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Node ID {node_id} doesn't exist", code="node_not_found", details={"node_id": node_id}
|
||||
)
|
||||
|
||||
def _get_closed_data(self, section, id_key):
|
||||
"""
|
||||
@@ -817,7 +819,9 @@ class Project:
|
||||
try:
|
||||
return self._drawings[drawing_id]
|
||||
except KeyError:
|
||||
raise ControllerNotFoundError(f"Drawing ID {drawing_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Drawing ID {drawing_id} doesn't exist", code="drawing_not_found", details={"drawing_id": drawing_id}
|
||||
)
|
||||
|
||||
@open_required
|
||||
async def delete_drawing(self, drawing_id):
|
||||
@@ -1101,7 +1105,9 @@ class Project:
|
||||
try:
|
||||
return self._links[link_id]
|
||||
except KeyError:
|
||||
raise ControllerNotFoundError(f"Link ID {link_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Link ID {link_id} doesn't exist", code="link_not_found", details={"link_id": link_id}
|
||||
)
|
||||
|
||||
@property
|
||||
def links(self):
|
||||
@@ -1462,7 +1468,11 @@ class Project:
|
||||
try:
|
||||
return self._snapshots[snapshot_id]
|
||||
except KeyError:
|
||||
raise ControllerNotFoundError(f"Snapshot ID {snapshot_id} doesn't exist")
|
||||
raise ControllerNotFoundError(
|
||||
f"Snapshot ID {snapshot_id} doesn't exist",
|
||||
code="snapshot_not_found",
|
||||
details={"snapshot_id": snapshot_id},
|
||||
)
|
||||
|
||||
def _load_snapshot_config(self):
|
||||
|
||||
|
||||
Reference in new issue
Block a user