RWTH-EBC / FiLiP

FIWARE Library for Python (FiLiP) to work with FIWARE API
BSD 3-Clause "New" or "Revised" License
23 stars 13 forks source link

Detailed Exception Handling inside different filip methods #305

Open RCX112 opened 1 month ago

RCX112 commented 1 month ago

Is your feature request related to a problem? Please describe.

Currently there is the problem that several filip methods do not throw detailed exceptions. For example, if I use the post_device or update_device method of the IoT Agent, only a simple request error with a HTTP status code is thrown in the event of an exception, without any further details:

iota_client.post_device(device=device1)

try:
    iota_client.post_device(device=device1)  # Exception because duplicate device
except Exception as e:
    print(e)

The output of the exception is only 409 Client Error: Conflict for url: http://localhost:4041/iot/devices with no more detailed information. Currently you have to use the debugger to find out that the exception is thrown by a duplicate Device in this case.

The same 409 error is thrown when I want to update a Device in the IoT-Agent (note that I used newer versions of the OCB and the IoT Agent):

iota_client.post_device(device=device1)

try:
    iota_client.update_device(device=device1)  # Exception because no device entity in OCB
except Exception as e:
    print(e)

Describe the solution you'd like

The filip functions currently raise only the status code, but it would be very useful to also raise the content of the request responses. We could maybe create a custom exception, which contains more detailed informations about the errors. The update_device method could look for example like this:

class DeviceUpdateException(Exception):
    def __init__(self, message, response):
        super().__init__(message)
        self.response = response

class IoTAClient(BaseHttpClient):
    def update_device(self, *, device: Device, add: bool = True) -> None:
        """
        Updates a device from the device registry.
        Adds, removes attributes from the device entry and changes
        attributes values.
        It does not change device settings (endpoint,..) and only adds
        attributes to the corresponding entity, their it does not
        change any attribute value and does not delete removed attributes

        Args:
            device:
            add (bool): If device not found add it
        Returns:
            None
        """
        url = urljoin(self.base_url, f'iot/devices/{device.device_id}')
        headers = self.headers
        try:
            res = self.put(url=url, headers=headers, json=device.model_dump(
                include={'attributes', 'lazy', 'commands', 'static_attributes'},
                exclude_none=True))
            if res.ok:
                self.logger.info("Device '%s' successfully updated!",
                                 device.device_id)
            elif (res.status_code == 404) & (add is True):
                self.post_device(device=device, update=False)
            else:
                res.raise_for_status()
        except requests.RequestException as err:
            msg = f"Could not update device '{device.device_id}'"
            self.log_error(err=err, msg=msg)
            raise DeviceUpdateException(msg, res) from err

Here DeviceUpdateException was added as a custom exception, which contains the request response as an attribute. We have indeed some use cases where the detailed exception handling would be very useful.

Additional context

github-actions[bot] commented 1 month ago

Branch 305-Detailed-Exception-Handling-inside-different-filip-methods created!