From 58b44c22717ad50cfd97fedda8af2489ca17dc7e Mon Sep 17 00:00:00 2001 From: Ludwig Schneider Date: Tue, 29 Aug 2023 14:45:18 -0400 Subject: [PATCH] Fix duplicated name error msg (#295) * Catching and identifying the situations where a duplicate Name is the issue in a save * introduce an inherent Error for duplicated names * upgrading reliability and no reference to namespaces anymore. --- src/cript/api/api.py | 14 +++++++++++++- src/cript/api/exceptions.py | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/cript/api/api.py b/src/cript/api/api.py index 17e5db5f8..416bae88c 100644 --- a/src/cript/api/api.py +++ b/src/cript/api/api.py @@ -16,6 +16,7 @@ CRIPTAPIRequiredError, CRIPTAPISaveError, CRIPTConnectionError, + CRIPTDuplicateNameError, InvalidHostError, InvalidVocabulary, ) @@ -683,7 +684,18 @@ def _internal_save(self, node, save_values: Optional[_InternalSaveValues] = None # If we get an error we may be able to fix, we to handle this extra and save the bad node first. # Errors with this code, may be fixable if response["code"] in (400, 409): - returned_save_values = _fix_node_save(self, node, response, save_values) + try: + returned_save_values = _fix_node_save(self, node, response, save_values) + except CRIPTAPISaveError as exc: + # If the previous error was a duplicated name issue + if "duplicate item [{'name':" in str(response["error"]): + # And (second condition) the request failed bc of the now suppressed name + if "'name' is a required property" in exc.api_response: + # Raise a save error, with the nice name related error message + raise CRIPTDuplicateNameError(response, json_data, exc) from exc + # Else just raise the exception as normal. + raise exc + save_values += returned_save_values # Handle errors from patching with too many attributes diff --git a/src/cript/api/exceptions.py b/src/cript/api/exceptions.py index 0dd7062c0..fbd3350c1 100644 --- a/src/cript/api/exceptions.py +++ b/src/cript/api/exceptions.py @@ -1,4 +1,5 @@ -from typing import List, Optional, Set +import json +from typing import List, Set from cript.exceptions import CRIPTException @@ -110,7 +111,7 @@ class CRIPTAPISaveError(CRIPTException): http_code: str api_response: str - def __init__(self, api_host_domain: str, http_code: str, api_response: str, patch_request: bool, pre_saved_nodes: Optional[Set[str]] = None, json_data: Optional[str] = None): + def __init__(self, api_host_domain: str, http_code: str, api_response: str, patch_request: bool, pre_saved_nodes: Set[str], json_data: str): self.api_host_domain = api_host_domain self.http_code = http_code self.api_response = api_response @@ -129,6 +130,35 @@ def __str__(self) -> str: return error_message +class CRIPTDuplicateNameError(CRIPTAPISaveError): + def __init__(self, api_response, json_data: str, parent_cript_save_error: CRIPTAPISaveError): + super().__init__( + parent_cript_save_error.api_host_domain, api_response["code"], api_response=api_response["error"], patch_request=parent_cript_save_error.patch_request, pre_saved_nodes=parent_cript_save_error.pre_saved_nodes, json_data=json_data + ) + + # We don't care if the data is invalid JSON + # So let's catch a couple of common exceptions and ensure still meaning error messages + # (and debug info in case it does happen.) + try: + json_dict = json.loads(self.json_data) + except (TypeError, json.JSONDecodeError): + self.name = "unknown_name" + self.node = "UnknownType" + try: + self.name = json_dict["name"] + except KeyError: + self.name = "unknown_name_key" + try: + self.node = json_dict["node"][0] + except KeyError: + self.node = "UnknownTypeKey" + except IndexError: + self.node = "UnknownTypeIdx" + + def __str__(self) -> str: + return f"The name '{self.name}' for your {self.node} node is already present in CRIPT. Either choose a new name!" # , or consider namespaces like 'MyNameSpace::{self.name}' instead." + + class InvalidHostError(CRIPTException): """ ## Definition